| .. | .. | 
|---|
 | 1 | +package timeflow.app;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.model.*;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +import java.awt.*;  | 
|---|
 | 6 | +import java.io.File;  | 
|---|
 | 7 | +import java.util.*;  | 
|---|
 | 8 | +import javax.imageio.*;  | 
|---|
 | 9 | +  | 
|---|
 | 10 | +public class AboutWindow extends Window {  | 
|---|
 | 11 | +	  | 
|---|
 | 12 | +	Image image;  | 
|---|
 | 13 | +	Display display;  | 
|---|
 | 14 | +	int w=640, h=380;  | 
|---|
 | 15 | +	  | 
|---|
 | 16 | +	public AboutWindow(Frame owner, Display display) {  | 
|---|
 | 17 | +		super(owner);  | 
|---|
 | 18 | +		this.display=display;  | 
|---|
 | 19 | +		  | 
|---|
 | 20 | +		try  | 
|---|
 | 21 | +		{  | 
|---|
 | 22 | +                    // image=ImageIO.read(getClass().getClassLoader().getResourceAsStream("images/splash.jpg"));  | 
|---|
 | 23 | +			image=ImageIO.read(new File("images/splash.jpg"));  | 
|---|
 | 24 | +			w=image.getWidth(null);  | 
|---|
 | 25 | +			h=image.getHeight(null);  | 
|---|
 | 26 | +		}  | 
|---|
 | 27 | +		catch (Exception e)  | 
|---|
 | 28 | +		{  | 
|---|
 | 29 | +			e.printStackTrace(System.out);  | 
|---|
 | 30 | +		}  | 
|---|
 | 31 | +		Dimension size = Toolkit.getDefaultToolkit().getScreenSize();  | 
|---|
 | 32 | +		setBounds((size.width-w)/2, (size.height-h)/2, w,h);  | 
|---|
 | 33 | +		  | 
|---|
 | 34 | +	}  | 
|---|
 | 35 | +	  | 
|---|
 | 36 | +	public void paint(Graphics g)  | 
|---|
 | 37 | +	{  | 
|---|
 | 38 | +		if (image!=null)  | 
|---|
 | 39 | +		{  | 
|---|
 | 40 | +			g.drawImage(image,0,0,null);  | 
|---|
 | 41 | +			return;  | 
|---|
 | 42 | +		}  | 
|---|
 | 43 | +		int lx=15;  | 
|---|
 | 44 | +		((Graphics2D)g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);  | 
|---|
 | 45 | +		g.setColor(display.getColor("splash.background"));  | 
|---|
 | 46 | +		g.fillRect(0,0,w,h);  | 
|---|
 | 47 | +		g.setFont(display.huge());  | 
|---|
 | 48 | +		g.setColor(display.getColor("splash.text"));  | 
|---|
 | 49 | +		g.drawString(Display.version(), lx, 35);  | 
|---|
 | 50 | +		g.setFont(display.plain());  | 
|---|
 | 51 | +		g.drawString("Prototype version",lx,60);  | 
|---|
 | 52 | +	}  | 
|---|
 | 53 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.util.*;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +import java.io.*;  | 
|---|
 | 6 | +import java.util.*;  | 
|---|
 | 7 | +  | 
|---|
 | 8 | +public class AppState  | 
|---|
 | 9 | +{  | 
|---|
 | 10 | +  | 
|---|
 | 11 | +        private static final String FILE = "settings/info.txt";  | 
|---|
 | 12 | +        private File currentFile, currentDir;  | 
|---|
 | 13 | +        private LinkedList<File> recentFiles = new LinkedList<File>();  | 
|---|
 | 14 | +  | 
|---|
 | 15 | +        public AppState()  | 
|---|
 | 16 | +        {  | 
|---|
 | 17 | +                if (!new File(FILE).exists())  | 
|---|
 | 18 | +                {  | 
|---|
 | 19 | +                        System.err.println("No existing settings file found.");  | 
|---|
 | 20 | +                        return;  | 
|---|
 | 21 | +                }  | 
|---|
 | 22 | +                try  | 
|---|
 | 23 | +                {  | 
|---|
 | 24 | +                        for (String line : IO.lines(FILE))  | 
|---|
 | 25 | +                        {  | 
|---|
 | 26 | +                                String[] t = line.split("\t");  | 
|---|
 | 27 | +                                String command = t[0];  | 
|---|
 | 28 | +                                String arg = t[1];  | 
|---|
 | 29 | +                                if ("CURRENT_FILE".equals(command))  | 
|---|
 | 30 | +                                {  | 
|---|
 | 31 | +                                        currentFile = new File(arg);  | 
|---|
 | 32 | +                                } else if ("RECENT_FILE".equals(command))  | 
|---|
 | 33 | +                                {  | 
|---|
 | 34 | +                                        recentFiles.add(new File(arg).getAbsoluteFile());  | 
|---|
 | 35 | +                                } else if ("CURRENT_DIR".equals(command))  | 
|---|
 | 36 | +                                {  | 
|---|
 | 37 | +                                        currentDir = new File(arg);  | 
|---|
 | 38 | +                                }  | 
|---|
 | 39 | +                        }  | 
|---|
 | 40 | +                } catch (Exception e)  | 
|---|
 | 41 | +                {  | 
|---|
 | 42 | +                        e.printStackTrace(System.out);  | 
|---|
 | 43 | +                }  | 
|---|
 | 44 | +        }  | 
|---|
 | 45 | +  | 
|---|
 | 46 | +        public List<File> getRecentFiles()  | 
|---|
 | 47 | +        {  | 
|---|
 | 48 | +                return (List<File>) recentFiles.clone();  | 
|---|
 | 49 | +        }  | 
|---|
 | 50 | +  | 
|---|
 | 51 | +        public File getCurrentFile()  | 
|---|
 | 52 | +        {  | 
|---|
 | 53 | +                return currentFile;  | 
|---|
 | 54 | +        }  | 
|---|
 | 55 | +  | 
|---|
 | 56 | +        public void setCurrentFile(File currentFile)  | 
|---|
 | 57 | +        {  | 
|---|
 | 58 | +                this.currentFile = currentFile.getAbsoluteFile();  | 
|---|
 | 59 | +  | 
|---|
 | 60 | +                // if list is big, remove one at end.  | 
|---|
 | 61 | +                if (recentFiles.size() > 10)  | 
|---|
 | 62 | +                {  | 
|---|
 | 63 | +                        recentFiles.removeLast();  | 
|---|
 | 64 | +                }  | 
|---|
 | 65 | +  | 
|---|
 | 66 | +                // put at front of list  | 
|---|
 | 67 | +                if (recentFiles.contains(this.currentFile))  | 
|---|
 | 68 | +                {  | 
|---|
 | 69 | +                        recentFiles.remove(this.currentFile);  | 
|---|
 | 70 | +                }  | 
|---|
 | 71 | +                recentFiles.addFirst(this.currentFile);  | 
|---|
 | 72 | +  | 
|---|
 | 73 | +                // set current dir, too.  | 
|---|
 | 74 | +                this.currentDir = currentDir;  | 
|---|
 | 75 | +        }  | 
|---|
 | 76 | +  | 
|---|
 | 77 | +        public File getCurrentDir()  | 
|---|
 | 78 | +        {  | 
|---|
 | 79 | +                return currentDir;  | 
|---|
 | 80 | +        }  | 
|---|
 | 81 | +  | 
|---|
 | 82 | +        public void setCurrentDir(File currentDir)  | 
|---|
 | 83 | +        {  | 
|---|
 | 84 | +                this.currentDir = currentDir;  | 
|---|
 | 85 | +        }  | 
|---|
 | 86 | +  | 
|---|
 | 87 | +        public void save()  | 
|---|
 | 88 | +        {  | 
|---|
 | 89 | +                try  | 
|---|
 | 90 | +                {  | 
|---|
 | 91 | +                        FileOutputStream fos = new FileOutputStream(FILE);  | 
|---|
 | 92 | +                        PrintStream out = new PrintStream(fos);  | 
|---|
 | 93 | +                        out.println("CURRENT_FILE\t" + currentFile);  | 
|---|
 | 94 | +                        out.println("CURRENT_DIR\t" + currentDir);  | 
|---|
 | 95 | +                        for (File f : recentFiles)  | 
|---|
 | 96 | +                        {  | 
|---|
 | 97 | +                                out.println("RECENT_FILE\t" + f);  | 
|---|
 | 98 | +                        }  | 
|---|
 | 99 | +                        out.flush();  | 
|---|
 | 100 | +                        out.close();  | 
|---|
 | 101 | +                        fos.close();  | 
|---|
 | 102 | +                } catch (Exception e)  | 
|---|
 | 103 | +                {  | 
|---|
 | 104 | +                        e.printStackTrace(System.out);  | 
|---|
 | 105 | +                }  | 
|---|
 | 106 | +        }  | 
|---|
 | 107 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.app.ui.*;  | 
|---|
 | 4 | +import timeflow.app.actions.*;  | 
|---|
 | 5 | +import timeflow.app.ui.filter.*;  | 
|---|
 | 6 | +import timeflow.data.db.*;  | 
|---|
 | 7 | +import timeflow.data.time.RoughTime;  | 
|---|
 | 8 | +import timeflow.format.field.*;  | 
|---|
 | 9 | +import timeflow.format.file.*;  | 
|---|
 | 10 | +import timeflow.model.*;  | 
|---|
 | 11 | +import timeflow.views.*;  | 
|---|
 | 12 | +  | 
|---|
 | 13 | +import javax.swing.*;  | 
|---|
 | 14 | +import javax.swing.event.ChangeEvent;  | 
|---|
 | 15 | +import javax.swing.event.ChangeListener;  | 
|---|
 | 16 | +  | 
|---|
 | 17 | +import timeflow.util.Pad;  | 
|---|
 | 18 | +  | 
|---|
 | 19 | +import java.awt.*;  | 
|---|
 | 20 | +import java.awt.event.*;  | 
|---|
 | 21 | +import java.beans.PropertyChangeEvent;  | 
|---|
 | 22 | +import java.beans.PropertyChangeListener;  | 
|---|
 | 23 | +import java.io.*;  | 
|---|
 | 24 | +import java.util.ArrayList;  | 
|---|
 | 25 | +  | 
|---|
 | 26 | +public class TimeflowApp extends JFrame  | 
|---|
 | 27 | +{  | 
|---|
 | 28 | +  | 
|---|
 | 29 | +        public TFModel model = new TFModel();  | 
|---|
 | 30 | +        public JFileChooser fileChooser;  | 
|---|
 | 31 | +        AboutWindow splash;  | 
|---|
 | 32 | +        String[][] examples;  | 
|---|
 | 33 | +        String[] templates;  | 
|---|
 | 34 | +        AppState state = new AppState();  | 
|---|
 | 35 | +        JMenu openRecent = new JMenu("Open Recent");  | 
|---|
 | 36 | +        public JMenu filterMenu;  | 
|---|
 | 37 | +        JMenuItem save = new JMenuItem("Save");  | 
|---|
 | 38 | +        FilterControlPanel filterControlPanel;  | 
|---|
 | 39 | +        LinkTabPane leftPanel;  | 
|---|
 | 40 | +        TFListener filterMenuMaker = new TFListener()  | 
|---|
 | 41 | +        {  | 
|---|
 | 42 | +  | 
|---|
 | 43 | +                @Override  | 
|---|
 | 44 | +                public void note(TFEvent e)  | 
|---|
 | 45 | +                {  | 
|---|
 | 46 | +                        if (e.affectsSchema())  | 
|---|
 | 47 | +                        {  | 
|---|
 | 48 | +                                filterMenu.removeAll();  | 
|---|
 | 49 | +                                for (Field f : model.getDB().getFields())  | 
|---|
 | 50 | +                                {  | 
|---|
 | 51 | +                                        if (f.getType() == String.class || f.getType() == String[].class  | 
|---|
 | 52 | +                                                || f.getType() == Double.class || f.getType() == RoughTime.class)  | 
|---|
 | 53 | +                                        {  | 
|---|
 | 54 | +                                                final JCheckBoxMenuItem item = new JCheckBoxMenuItem(f.getName());  | 
|---|
 | 55 | +                                                final Field field = f;  | 
|---|
 | 56 | +                                                filterMenu.add(item);  | 
|---|
 | 57 | +                                                item.addActionListener(new ActionListener()  | 
|---|
 | 58 | +                                                {  | 
|---|
 | 59 | +  | 
|---|
 | 60 | +                                                        @Override  | 
|---|
 | 61 | +                                                        public void actionPerformed(ActionEvent e)  | 
|---|
 | 62 | +                                                        {  | 
|---|
 | 63 | +                                                                filterControlPanel.setFacet(field, item.getState());  | 
|---|
 | 64 | +                                                                leftPanel.setSelectedIndex(1);  | 
|---|
 | 65 | +                                                        }  | 
|---|
 | 66 | +                                                });  | 
|---|
 | 67 | +                                        }  | 
|---|
 | 68 | +                                }  | 
|---|
 | 69 | +                        }  | 
|---|
 | 70 | +                }  | 
|---|
 | 71 | +        };  | 
|---|
 | 72 | +  | 
|---|
 | 73 | +        void splash(boolean visible)  | 
|---|
 | 74 | +        {  | 
|---|
 | 75 | +                splash.setVisible(visible);  | 
|---|
 | 76 | +        }  | 
|---|
 | 77 | +  | 
|---|
 | 78 | +        public void init() throws Exception  | 
|---|
 | 79 | +        {  | 
|---|
 | 80 | +                Dimension d = Toolkit.getDefaultToolkit().getScreenSize();  | 
|---|
 | 81 | +                setBounds(0, 0, Math.min(d.width, 1200), Math.min(d.height, 900));  | 
|---|
 | 82 | +                setTitle(Display.version());  | 
|---|
 | 83 | +                setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);  | 
|---|
 | 84 | +                final QuitAction quitAction = new QuitAction(this, model);  | 
|---|
 | 85 | +                addWindowListener(new WindowAdapter()  | 
|---|
 | 86 | +                {  | 
|---|
 | 87 | +  | 
|---|
 | 88 | +                        @Override  | 
|---|
 | 89 | +                        public void windowClosing(WindowEvent e)  | 
|---|
 | 90 | +                        {  | 
|---|
 | 91 | +                                quitAction.quit();  | 
|---|
 | 92 | +                        }  | 
|---|
 | 93 | +  | 
|---|
 | 94 | +                        public void windowStateChanged(WindowEvent e)  | 
|---|
 | 95 | +                        {  | 
|---|
 | 96 | +                                repaint();  | 
|---|
 | 97 | +                        }  | 
|---|
 | 98 | +                });  | 
|---|
 | 99 | +                Image icon = Toolkit.getDefaultToolkit().getImage("images/icon.gif");  | 
|---|
 | 100 | +                setIconImage(icon);  | 
|---|
 | 101 | +  | 
|---|
 | 102 | +                // read example directory  | 
|---|
 | 103 | +                String[] ex = getVisibleFiles("settings/examples");  | 
|---|
 | 104 | +                int n = ex.length;  | 
|---|
 | 105 | +                examples = new String[n][2];  | 
|---|
 | 106 | +                for (int i = 0; i < n; i++)  | 
|---|
 | 107 | +                {  | 
|---|
 | 108 | +                        String s = ex[i];  | 
|---|
 | 109 | +                        int dot = s.lastIndexOf('.');  | 
|---|
 | 110 | +                        if (dot >= 0 && dot < s.length() - 1);  | 
|---|
 | 111 | +                        s = s.substring(0, dot);  | 
|---|
 | 112 | +                        examples[i][0] = s;  | 
|---|
 | 113 | +                        examples[i][1] = "settings/examples/" + ex[i];  | 
|---|
 | 114 | +                }  | 
|---|
 | 115 | +                templates = getVisibleFiles("settings/templates");  | 
|---|
 | 116 | +                fileChooser = new JFileChooser(state.getCurrentFile());  | 
|---|
 | 117 | +  | 
|---|
 | 118 | +                getContentPane().setLayout(new BorderLayout());  | 
|---|
 | 119 | +  | 
|---|
 | 120 | +                // left tab area, with vertical gray divider.  | 
|---|
 | 121 | +                JPanel leftHolder = new JPanel();  | 
|---|
 | 122 | +                getContentPane().add(leftHolder, BorderLayout.WEST);  | 
|---|
 | 123 | +  | 
|---|
 | 124 | +                leftHolder.setLayout(new BorderLayout());  | 
|---|
 | 125 | +                JPanel pad = new Pad(3, 3);  | 
|---|
 | 126 | +                pad.setBackground(Color.gray);  | 
|---|
 | 127 | +                leftHolder.add(pad, BorderLayout.EAST);  | 
|---|
 | 128 | +  | 
|---|
 | 129 | +                leftPanel = new LinkTabPane();//JTabbedPane();  | 
|---|
 | 130 | +                leftHolder.add(leftPanel, BorderLayout.CENTER);  | 
|---|
 | 131 | +  | 
|---|
 | 132 | +                JPanel configPanel = new JPanel();  | 
|---|
 | 133 | +                configPanel.setLayout(new BorderLayout());  | 
|---|
 | 134 | +                filterMenu = new JMenu("Filters");  | 
|---|
 | 135 | +                filterControlPanel = new FilterControlPanel(model, filterMenu);  | 
|---|
 | 136 | +                final GlobalDisplayPanel displayPanel = new GlobalDisplayPanel(model, filterControlPanel);  | 
|---|
 | 137 | +                configPanel.add(displayPanel, BorderLayout.NORTH);  | 
|---|
 | 138 | +  | 
|---|
 | 139 | +                JPanel legend = new JPanel();  | 
|---|
 | 140 | +                legend.setLayout(new BorderLayout());  | 
|---|
 | 141 | +                configPanel.add(legend, BorderLayout.CENTER);  | 
|---|
 | 142 | +                legend.add(new SizeLegendPanel(model), BorderLayout.NORTH);  | 
|---|
 | 143 | +                legend.add(new ColorLegendPanel(model), BorderLayout.CENTER);  | 
|---|
 | 144 | +                leftPanel.addTab(configPanel, "Display", true);  | 
|---|
 | 145 | +  | 
|---|
 | 146 | +                leftPanel.addTab(filterControlPanel, "Filter", true);  | 
|---|
 | 147 | +  | 
|---|
 | 148 | +                // center tab area  | 
|---|
 | 149 | +  | 
|---|
 | 150 | +                final LinkTabPane center = new LinkTabPane();  | 
|---|
 | 151 | +                getContentPane().add(center, BorderLayout.CENTER);  | 
|---|
 | 152 | +  | 
|---|
 | 153 | +                center.addPropertyChangeListener(new PropertyChangeListener()  | 
|---|
 | 154 | +                {  | 
|---|
 | 155 | +  | 
|---|
 | 156 | +                        @Override  | 
|---|
 | 157 | +                        public void propertyChange(PropertyChangeEvent evt)  | 
|---|
 | 158 | +                        {  | 
|---|
 | 159 | +                                displayPanel.showLocalControl(center.getCurrentName());  | 
|---|
 | 160 | +                        }  | 
|---|
 | 161 | +                });  | 
|---|
 | 162 | +  | 
|---|
 | 163 | +                final IntroView intro = new IntroView(model); // we refer to this a bit later.  | 
|---|
 | 164 | +                final TimelineView timeline = new TimelineView(model);  | 
|---|
 | 165 | +                AbstractView[] views =  | 
|---|
 | 166 | +                {  | 
|---|
 | 167 | +                        timeline,  | 
|---|
 | 168 | +                        new CalendarView(model),  | 
|---|
 | 169 | +                        new ListView(model),  | 
|---|
 | 170 | +                        new TableView(model),  | 
|---|
 | 171 | +                        new BarGraphView(model),  | 
|---|
 | 172 | +                        intro,  | 
|---|
 | 173 | +                        new DescriptionView(model),  | 
|---|
 | 174 | +                        new SummaryView(model),  | 
|---|
 | 175 | +                };  | 
|---|
 | 176 | +  | 
|---|
 | 177 | +                for (int i = 0; i < views.length; i++)  | 
|---|
 | 178 | +                {  | 
|---|
 | 179 | +                        center.addTab(views[i], views[i].getName(), i < 5);  | 
|---|
 | 180 | +                        displayPanel.addLocalControl(views[i].getName(), views[i].getControls());  | 
|---|
 | 181 | +                }  | 
|---|
 | 182 | +  | 
|---|
 | 183 | +                // start off with intro screen  | 
|---|
 | 184 | +                center.setCurrentName(intro.getName());  | 
|---|
 | 185 | +                displayPanel.showLocalControl(intro.getName());  | 
|---|
 | 186 | +  | 
|---|
 | 187 | +                // but then, once data is loaded, switch directly to the timeline view.  | 
|---|
 | 188 | +                model.addListener(new TFListener()  | 
|---|
 | 189 | +                {  | 
|---|
 | 190 | +  | 
|---|
 | 191 | +                        @Override  | 
|---|
 | 192 | +                        public void note(TFEvent e)  | 
|---|
 | 193 | +                        {  | 
|---|
 | 194 | +                                if (e.type == e.type.DATABASE_CHANGE)  | 
|---|
 | 195 | +                                {  | 
|---|
 | 196 | +                                        if (center.getCurrentName().equals(intro.getName()))  | 
|---|
 | 197 | +                                        {  | 
|---|
 | 198 | +                                                center.setCurrentName(timeline.getName());  | 
|---|
 | 199 | +                                                displayPanel.showLocalControl(timeline.getName());  | 
|---|
 | 200 | +                                        }  | 
|---|
 | 201 | +                                }  | 
|---|
 | 202 | +                        }  | 
|---|
 | 203 | +                });  | 
|---|
 | 204 | +  | 
|---|
 | 205 | +                JMenuBar menubar = new JMenuBar();  | 
|---|
 | 206 | +                setJMenuBar(menubar);  | 
|---|
 | 207 | +  | 
|---|
 | 208 | +                JMenu fileMenu = new JMenu("File");  | 
|---|
 | 209 | +                menubar.add(fileMenu);  | 
|---|
 | 210 | +  | 
|---|
 | 211 | +                fileMenu.add(new NewDataAction(this));  | 
|---|
 | 212 | +                fileMenu.add(new CopySchemaAction(this));  | 
|---|
 | 213 | +  | 
|---|
 | 214 | +                JMenu templateMenu = new JMenu("New From Template");  | 
|---|
 | 215 | +                fileMenu.add(templateMenu);  | 
|---|
 | 216 | +                for (int i = 0; i < templates.length; i++)  | 
|---|
 | 217 | +                {  | 
|---|
 | 218 | +                        JMenuItem t = new JMenuItem(templates[i]);  | 
|---|
 | 219 | +                        final String fileName = "settings/templates/" + templates[i];  | 
|---|
 | 220 | +                        templateMenu.add(t);  | 
|---|
 | 221 | +                        t.addActionListener(new ActionListener()  | 
|---|
 | 222 | +                        {  | 
|---|
 | 223 | +  | 
|---|
 | 224 | +                                @Override  | 
|---|
 | 225 | +                                public void actionPerformed(ActionEvent e)  | 
|---|
 | 226 | +                                {  | 
|---|
 | 227 | +                                        load(fileName, FileExtensionCatalog.get(fileName), true);  | 
|---|
 | 228 | +                                }  | 
|---|
 | 229 | +                        });  | 
|---|
 | 230 | +                }  | 
|---|
 | 231 | +  | 
|---|
 | 232 | +                fileMenu.addSeparator();  | 
|---|
 | 233 | +  | 
|---|
 | 234 | +  | 
|---|
 | 235 | +                JMenuItem open = new JMenuItem("Open...");  | 
|---|
 | 236 | +                fileMenu.add(open);  | 
|---|
 | 237 | +                open.addActionListener(new ActionListener()  | 
|---|
 | 238 | +                {  | 
|---|
 | 239 | +  | 
|---|
 | 240 | +                        @Override  | 
|---|
 | 241 | +                        public void actionPerformed(ActionEvent e)  | 
|---|
 | 242 | +                        {  | 
|---|
 | 243 | +                                load(new TimeflowFormat(), false);  | 
|---|
 | 244 | +                        }  | 
|---|
 | 245 | +                });  | 
|---|
 | 246 | +  | 
|---|
 | 247 | +  | 
|---|
 | 248 | +                fileMenu.add(openRecent);  | 
|---|
 | 249 | +                makeRecentFileMenu();  | 
|---|
 | 250 | +                fileMenu.addSeparator();  | 
|---|
 | 251 | +                fileMenu.add(new ImportFromPasteAction(this));  | 
|---|
 | 252 | +  | 
|---|
 | 253 | +                JMenuItem impDel = new JMenuItem("Import CSV/TSV...");  | 
|---|
 | 254 | +                fileMenu.add(impDel);  | 
|---|
 | 255 | +                impDel.addActionListener(new ActionListener()  | 
|---|
 | 256 | +                {  | 
|---|
 | 257 | +  | 
|---|
 | 258 | +                        @Override  | 
|---|
 | 259 | +                        public void actionPerformed(ActionEvent e)  | 
|---|
 | 260 | +                        {  | 
|---|
 | 261 | +                                if (checkSaveStatus())  | 
|---|
 | 262 | +                                {  | 
|---|
 | 263 | +                                        importDelimited();  | 
|---|
 | 264 | +                                }  | 
|---|
 | 265 | +                        }  | 
|---|
 | 266 | +                });  | 
|---|
 | 267 | +  | 
|---|
 | 268 | +                fileMenu.addSeparator();  | 
|---|
 | 269 | +  | 
|---|
 | 270 | +                fileMenu.add(save);  | 
|---|
 | 271 | +                save.setAccelerator(KeyStroke.getKeyStroke('S',  | 
|---|
 | 272 | +                        Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));  | 
|---|
 | 273 | +                save.setEnabled(false);  | 
|---|
 | 274 | +                save.addActionListener(new ActionListener()  | 
|---|
 | 275 | +                {  | 
|---|
 | 276 | +  | 
|---|
 | 277 | +                        @Override  | 
|---|
 | 278 | +                        public void actionPerformed(ActionEvent e)  | 
|---|
 | 279 | +                        {  | 
|---|
 | 280 | +                                save(model.getDbFile());  | 
|---|
 | 281 | +  | 
|---|
 | 282 | +                        }  | 
|---|
 | 283 | +                });  | 
|---|
 | 284 | +                model.addListener(new TFListener()  | 
|---|
 | 285 | +                {  | 
|---|
 | 286 | +  | 
|---|
 | 287 | +                        @Override  | 
|---|
 | 288 | +                        public void note(TFEvent e)  | 
|---|
 | 289 | +                        {  | 
|---|
 | 290 | +                                save.setEnabled(!model.getReadOnly());  | 
|---|
 | 291 | +                        }  | 
|---|
 | 292 | +                });  | 
|---|
 | 293 | +  | 
|---|
 | 294 | +                JMenuItem saveAs = new JMenuItem("Save As...");  | 
|---|
 | 295 | +                fileMenu.add(saveAs);  | 
|---|
 | 296 | +                saveAs.addActionListener(new ActionListener()  | 
|---|
 | 297 | +                {  | 
|---|
 | 298 | +  | 
|---|
 | 299 | +                        @Override  | 
|---|
 | 300 | +                        public void actionPerformed(ActionEvent e)  | 
|---|
 | 301 | +                        {  | 
|---|
 | 302 | +                                saveAs();  | 
|---|
 | 303 | +                        }  | 
|---|
 | 304 | +                });  | 
|---|
 | 305 | +  | 
|---|
 | 306 | +                fileMenu.addSeparator();  | 
|---|
 | 307 | +  | 
|---|
 | 308 | +                JMenuItem exportTSV = new JMenuItem("Export TSV...");  | 
|---|
 | 309 | +                fileMenu.add(exportTSV);  | 
|---|
 | 310 | +                exportTSV.addActionListener(new ActionListener()  | 
|---|
 | 311 | +                {  | 
|---|
 | 312 | +  | 
|---|
 | 313 | +                        @Override  | 
|---|
 | 314 | +                        public void actionPerformed(ActionEvent e)  | 
|---|
 | 315 | +                        {  | 
|---|
 | 316 | +                                exportDelimited('\t');  | 
|---|
 | 317 | +                        }  | 
|---|
 | 318 | +                });  | 
|---|
 | 319 | +                JMenuItem exportCSV = new JMenuItem("Export CSV...");  | 
|---|
 | 320 | +                fileMenu.add(exportCSV);  | 
|---|
 | 321 | +                exportCSV.addActionListener(new ActionListener()  | 
|---|
 | 322 | +                {  | 
|---|
 | 323 | +  | 
|---|
 | 324 | +                        @Override  | 
|---|
 | 325 | +                        public void actionPerformed(ActionEvent e)  | 
|---|
 | 326 | +                        {  | 
|---|
 | 327 | +                                exportDelimited(',');  | 
|---|
 | 328 | +                        }  | 
|---|
 | 329 | +                });  | 
|---|
 | 330 | +                JMenuItem exportHTML = new JMenuItem("Export HTML...");  | 
|---|
 | 331 | +                fileMenu.add(exportHTML);  | 
|---|
 | 332 | +                exportHTML.addActionListener(new ActionListener()  | 
|---|
 | 333 | +                {  | 
|---|
 | 334 | +  | 
|---|
 | 335 | +                        @Override  | 
|---|
 | 336 | +                        public void actionPerformed(ActionEvent e)  | 
|---|
 | 337 | +                        {  | 
|---|
 | 338 | +                                exportHtml();  | 
|---|
 | 339 | +                        }  | 
|---|
 | 340 | +                });  | 
|---|
 | 341 | +                fileMenu.addSeparator();  | 
|---|
 | 342 | +                fileMenu.add(quitAction);  | 
|---|
 | 343 | +  | 
|---|
 | 344 | +                JMenu editMenu = new JMenu("Edit");  | 
|---|
 | 345 | +                menubar.add(editMenu);  | 
|---|
 | 346 | +                editMenu.add(new AddRecordAction(this));  | 
|---|
 | 347 | +                editMenu.addSeparator();  | 
|---|
 | 348 | +                editMenu.add(new DateFieldAction(this));  | 
|---|
 | 349 | +                editMenu.add(new AddFieldAction(this));  | 
|---|
 | 350 | +                editMenu.add(new RenameFieldAction(this));  | 
|---|
 | 351 | +                editMenu.add(new DeleteFieldAction(this));  | 
|---|
 | 352 | +                editMenu.add(new ReorderFieldsAction(this));  | 
|---|
 | 353 | +                editMenu.addSeparator();  | 
|---|
 | 354 | +                editMenu.add(new EditSourceAction(this));  | 
|---|
 | 355 | +                editMenu.addSeparator();  | 
|---|
 | 356 | +                editMenu.add(new DeleteSelectedAction(this));  | 
|---|
 | 357 | +                editMenu.add(new DeleteUnselectedAction(this));  | 
|---|
 | 358 | +  | 
|---|
 | 359 | +                menubar.add(filterMenu);  | 
|---|
 | 360 | +                model.addListener(filterMenuMaker);  | 
|---|
 | 361 | +  | 
|---|
 | 362 | +  | 
|---|
 | 363 | +                JMenu exampleMenu = new JMenu("Examples");  | 
|---|
 | 364 | +                menubar.add(exampleMenu);  | 
|---|
 | 365 | +  | 
|---|
 | 366 | +                for (int i = 0; i < examples.length; i++)  | 
|---|
 | 367 | +                {  | 
|---|
 | 368 | +                        JMenuItem example = new JMenuItem(examples[i][0]);  | 
|---|
 | 369 | +                        exampleMenu.add(example);  | 
|---|
 | 370 | +                        final String file = examples[i][1];  | 
|---|
 | 371 | +                        example.addActionListener(new ActionListener()  | 
|---|
 | 372 | +                        {  | 
|---|
 | 373 | +  | 
|---|
 | 374 | +                                @Override  | 
|---|
 | 375 | +                                public void actionPerformed(ActionEvent e)  | 
|---|
 | 376 | +                                {  | 
|---|
 | 377 | +                                        load(file, FileExtensionCatalog.get(file), true);  | 
|---|
 | 378 | +                                }  | 
|---|
 | 379 | +                        });  | 
|---|
 | 380 | +                }  | 
|---|
 | 381 | +  | 
|---|
 | 382 | +                JMenu helpMenu = new JMenu("Help");  | 
|---|
 | 383 | +                menubar.add(helpMenu);  | 
|---|
 | 384 | +  | 
|---|
 | 385 | +                helpMenu.add(new WebDocAction(this));  | 
|---|
 | 386 | +  | 
|---|
 | 387 | +                JMenuItem about = new JMenuItem("About TimeFlow");  | 
|---|
 | 388 | +                helpMenu.add(about);  | 
|---|
 | 389 | +                about.addActionListener(new ActionListener()  | 
|---|
 | 390 | +                {  | 
|---|
 | 391 | +  | 
|---|
 | 392 | +                        @Override  | 
|---|
 | 393 | +                        public void actionPerformed(ActionEvent e)  | 
|---|
 | 394 | +                        {  | 
|---|
 | 395 | +                                splash(true);  | 
|---|
 | 396 | +                        }  | 
|---|
 | 397 | +                });  | 
|---|
 | 398 | +  | 
|---|
 | 399 | +                model.addListener(new TFListener()  | 
|---|
 | 400 | +                {  | 
|---|
 | 401 | +  | 
|---|
 | 402 | +                        @Override  | 
|---|
 | 403 | +                        public void note(TFEvent e)  | 
|---|
 | 404 | +                        {  | 
|---|
 | 405 | +                                if (e.type == TFEvent.Type.DATABASE_CHANGE)  | 
|---|
 | 406 | +                                {  | 
|---|
 | 407 | +                                        String name = model.getDbFile();  | 
|---|
 | 408 | +                                        int n = Math.max(name.lastIndexOf('/'), name.lastIndexOf('\\'));  | 
|---|
 | 409 | +                                        if (n > 0)  | 
|---|
 | 410 | +                                        {  | 
|---|
 | 411 | +                                                name = name.substring(n + 1);  | 
|---|
 | 412 | +                                        }  | 
|---|
 | 413 | +                                        setTitle(name);  | 
|---|
 | 414 | +                                }  | 
|---|
 | 415 | +                        }  | 
|---|
 | 416 | +                });  | 
|---|
 | 417 | +        }  | 
|---|
 | 418 | +  | 
|---|
 | 419 | +        void makeRecentFileMenu()  | 
|---|
 | 420 | +        {  | 
|---|
 | 421 | +                openRecent.removeAll();  | 
|---|
 | 422 | +                try  | 
|---|
 | 423 | +                {  | 
|---|
 | 424 | +                        for (File f : state.getRecentFiles())  | 
|---|
 | 425 | +                        {  | 
|---|
 | 426 | +                                final String file = f.getAbsolutePath();  | 
|---|
 | 427 | +                                JMenuItem m = new JMenuItem(f.getName());  | 
|---|
 | 428 | +                                openRecent.add(m);  | 
|---|
 | 429 | +                                m.addActionListener(new ActionListener()  | 
|---|
 | 430 | +                                {  | 
|---|
 | 431 | +  | 
|---|
 | 432 | +                                        @Override  | 
|---|
 | 433 | +                                        public void actionPerformed(ActionEvent e)  | 
|---|
 | 434 | +                                        {  | 
|---|
 | 435 | +                                                load(file, FileExtensionCatalog.get(file), false);  | 
|---|
 | 436 | +                                        }  | 
|---|
 | 437 | +                                });  | 
|---|
 | 438 | +                        }  | 
|---|
 | 439 | +                } catch (Exception e)  | 
|---|
 | 440 | +                {  | 
|---|
 | 441 | +                        e.printStackTrace(System.out);  | 
|---|
 | 442 | +                }  | 
|---|
 | 443 | +        }  | 
|---|
 | 444 | +  | 
|---|
 | 445 | +        void exportHtml()  | 
|---|
 | 446 | +        {  | 
|---|
 | 447 | +                int retval = fileChooser.showSaveDialog(this);  | 
|---|
 | 448 | +                if (retval == fileChooser.APPROVE_OPTION)  | 
|---|
 | 449 | +                {  | 
|---|
 | 450 | +                        String fileName = fileChooser.getSelectedFile().getAbsolutePath();  | 
|---|
 | 451 | +                        try  | 
|---|
 | 452 | +                        {  | 
|---|
 | 453 | +                                FileWriter fw = new FileWriter(fileName);  | 
|---|
 | 454 | +                                BufferedWriter out = new BufferedWriter(fw);  | 
|---|
 | 455 | +                                new HtmlFormat().export(model, out);  | 
|---|
 | 456 | +                                out.close();  | 
|---|
 | 457 | +                                fw.close();  | 
|---|
 | 458 | +                        } catch (Exception e)  | 
|---|
 | 459 | +                        {  | 
|---|
 | 460 | +                                System.out.println(e);  | 
|---|
 | 461 | +                                showUserError("Couldn't save file: " + e);  | 
|---|
 | 462 | +                        }  | 
|---|
 | 463 | +                }  | 
|---|
 | 464 | +        }  | 
|---|
 | 465 | +  | 
|---|
 | 466 | +        void exportDelimited(char delimiter)  | 
|---|
 | 467 | +        {  | 
|---|
 | 468 | +                int retval = fileChooser.showSaveDialog(this);  | 
|---|
 | 469 | +                if (retval == fileChooser.APPROVE_OPTION)  | 
|---|
 | 470 | +                {  | 
|---|
 | 471 | +                        String fileName = fileChooser.getSelectedFile().getAbsolutePath();  | 
|---|
 | 472 | +                        try  | 
|---|
 | 473 | +                        {  | 
|---|
 | 474 | +                                new DelimitedFormat(delimiter).write(model.getDB(), new File(fileName));  | 
|---|
 | 475 | +                        } catch (Exception e)  | 
|---|
 | 476 | +                        {  | 
|---|
 | 477 | +                                System.out.println(e);  | 
|---|
 | 478 | +                                showUserError("Couldn't save file: " + e);  | 
|---|
 | 479 | +                        }  | 
|---|
 | 480 | +                }  | 
|---|
 | 481 | +        }  | 
|---|
 | 482 | +  | 
|---|
 | 483 | +        void load(Import importer, boolean readOnly)  | 
|---|
 | 484 | +        {  | 
|---|
 | 485 | +                if (!checkSaveStatus())  | 
|---|
 | 486 | +                {  | 
|---|
 | 487 | +                        return;  | 
|---|
 | 488 | +                }  | 
|---|
 | 489 | +                try  | 
|---|
 | 490 | +                {  | 
|---|
 | 491 | +                        int retval = fileChooser.showOpenDialog(this);  | 
|---|
 | 492 | +                        if (retval == fileChooser.APPROVE_OPTION)  | 
|---|
 | 493 | +                        {  | 
|---|
 | 494 | +                                load(fileChooser.getSelectedFile().getAbsolutePath(), importer, readOnly);  | 
|---|
 | 495 | +                                noteFileUse(fileChooser.getSelectedFile().getAbsolutePath());  | 
|---|
 | 496 | +                        }  | 
|---|
 | 497 | +                } catch (Exception e)  | 
|---|
 | 498 | +                {  | 
|---|
 | 499 | +                        showUserError("Couldn't read file.");  | 
|---|
 | 500 | +                        System.out.println(e);  | 
|---|
 | 501 | +                }  | 
|---|
 | 502 | +        }  | 
|---|
 | 503 | +  | 
|---|
 | 504 | +        public void showImportEditor(String fileName, String[][] data)  | 
|---|
 | 505 | +        {  | 
|---|
 | 506 | +                final ImportDelimitedPanel editor = new ImportDelimitedPanel(model);  | 
|---|
 | 507 | +                editor.setFileName(fileName);  | 
|---|
 | 508 | +                editor.setData(data);  | 
|---|
 | 509 | +                editor.setBounds(0, 0, 1024, 768);  | 
|---|
 | 510 | +                editor.setVisible(true);  | 
|---|
 | 511 | +                SwingUtilities.invokeLater(new Runnable()  | 
|---|
 | 512 | +                {  | 
|---|
 | 513 | +  | 
|---|
 | 514 | +                        public void run()  | 
|---|
 | 515 | +                        {  | 
|---|
 | 516 | +                                editor.scrollToTop();  | 
|---|
 | 517 | +                        }  | 
|---|
 | 518 | +                });  | 
|---|
 | 519 | +  | 
|---|
 | 520 | +        }  | 
|---|
 | 521 | +  | 
|---|
 | 522 | +        void importDelimited()  | 
|---|
 | 523 | +        {  | 
|---|
 | 524 | +                if (!checkSaveStatus())  | 
|---|
 | 525 | +                {  | 
|---|
 | 526 | +                        return;  | 
|---|
 | 527 | +                }  | 
|---|
 | 528 | +                try  | 
|---|
 | 529 | +                {  | 
|---|
 | 530 | +                        int result = fileChooser.showOpenDialog(this);  | 
|---|
 | 531 | +  | 
|---|
 | 532 | +                        if (result == JFileChooser.APPROVE_OPTION)  | 
|---|
 | 533 | +                        {  | 
|---|
 | 534 | +                                File file = fileChooser.getSelectedFile();  | 
|---|
 | 535 | +                                String fileName = file.getAbsolutePath();  | 
|---|
 | 536 | +                                noteFileUse(fileName);  | 
|---|
 | 537 | +                                String[][] data = DelimitedFormat.readArrayGuessDelim(fileName, System.out);  | 
|---|
 | 538 | +                                showImportEditor(fileName, data);  | 
|---|
 | 539 | +  | 
|---|
 | 540 | +                        } else  | 
|---|
 | 541 | +                        {  | 
|---|
 | 542 | +                                System.out.println("OK, canceling import.");  | 
|---|
 | 543 | +                        }  | 
|---|
 | 544 | +                } catch (Exception e)  | 
|---|
 | 545 | +                {  | 
|---|
 | 546 | +                        showUserError("Couldn't read file format.");  | 
|---|
 | 547 | +                        e.printStackTrace(System.out);  | 
|---|
 | 548 | +                }  | 
|---|
 | 549 | +        }  | 
|---|
 | 550 | +  | 
|---|
 | 551 | +        void load(final String fileName, final Import importer, boolean readOnly)  | 
|---|
 | 552 | +        {  | 
|---|
 | 553 | +                if (!checkSaveStatus())  | 
|---|
 | 554 | +                {  | 
|---|
 | 555 | +                        return;  | 
|---|
 | 556 | +                }  | 
|---|
 | 557 | +                try  | 
|---|
 | 558 | +                {  | 
|---|
 | 559 | +                        final File f = new File(fileName);  | 
|---|
 | 560 | +                        ActDB db = importer.importFile(f);  | 
|---|
 | 561 | +                        model.setDB(db, fileName, readOnly, TimeflowApp.this);  | 
|---|
 | 562 | +                        if (!readOnly)  | 
|---|
 | 563 | +                        {  | 
|---|
 | 564 | +                                noteFileUse(fileName);  | 
|---|
 | 565 | +                        }  | 
|---|
 | 566 | +                } catch (Exception e)  | 
|---|
 | 567 | +                {  | 
|---|
 | 568 | +                        e.printStackTrace(System.out);  | 
|---|
 | 569 | +                        showUserError("Couldn't read file.");  | 
|---|
 | 570 | +                        model.noteError(this);  | 
|---|
 | 571 | +                }  | 
|---|
 | 572 | +        }  | 
|---|
 | 573 | +  | 
|---|
 | 574 | +        public boolean save(String fileName)  | 
|---|
 | 575 | +        {  | 
|---|
 | 576 | +                try  | 
|---|
 | 577 | +                {  | 
|---|
 | 578 | +                        FileWriter fw = new FileWriter(fileName);  | 
|---|
 | 579 | +                        BufferedWriter out = new BufferedWriter(fw);  | 
|---|
 | 580 | +                        new TimeflowFormat().export(model, out);  | 
|---|
 | 581 | +                        out.close();  | 
|---|
 | 582 | +                        fw.close();  | 
|---|
 | 583 | +                        noteFileUse(fileName);  | 
|---|
 | 584 | +                        if (!fileName.equals(model.getDbFile()))  | 
|---|
 | 585 | +                        {  | 
|---|
 | 586 | +                                model.setDbFile(fileName, false, this);  | 
|---|
 | 587 | +                        }  | 
|---|
 | 588 | +                        model.setChangedSinceSave(false);  | 
|---|
 | 589 | +                        model.setReadOnly(false);  | 
|---|
 | 590 | +                        save.setEnabled(true);  | 
|---|
 | 591 | +                        return true;  | 
|---|
 | 592 | +                } catch (Exception e)  | 
|---|
 | 593 | +                {  | 
|---|
 | 594 | +                        e.printStackTrace(System.out);  | 
|---|
 | 595 | +                        showUserError("Couldn't save file: " + e);  | 
|---|
 | 596 | +                        return false;  | 
|---|
 | 597 | +                }  | 
|---|
 | 598 | +        }  | 
|---|
 | 599 | +  | 
|---|
 | 600 | +        public boolean checkSaveStatus()  | 
|---|
 | 601 | +        {  | 
|---|
 | 602 | +                boolean needSave = model.isChangedSinceSave();  | 
|---|
 | 603 | +                if (!needSave)  | 
|---|
 | 604 | +                {  | 
|---|
 | 605 | +                        return true;  | 
|---|
 | 606 | +                }  | 
|---|
 | 607 | +  | 
|---|
 | 608 | +                Object[] options = null;  | 
|---|
 | 609 | +                if (model.isReadOnly())  | 
|---|
 | 610 | +                {  | 
|---|
 | 611 | +                        options = new Object[]  | 
|---|
 | 612 | +                        {  | 
|---|
 | 613 | +                                "Save As", "Discard Changes", "Cancel"  | 
|---|
 | 614 | +                        };  | 
|---|
 | 615 | +                } else  | 
|---|
 | 616 | +                {  | 
|---|
 | 617 | +                        options = new Object[]  | 
|---|
 | 618 | +                        {  | 
|---|
 | 619 | +                                "Save", "Save As", "Discard Changes", "Cancel"  | 
|---|
 | 620 | +                        };  | 
|---|
 | 621 | +                }  | 
|---|
 | 622 | +                int n = JOptionPane.showOptionDialog(  | 
|---|
 | 623 | +                        this,  | 
|---|
 | 624 | +                        "The current data set has unsaved changes that will be lost.\n"  | 
|---|
 | 625 | +                        + "Would you like to save them before continuing?",  | 
|---|
 | 626 | +                        "Save Before Closing?",  | 
|---|
 | 627 | +                        JOptionPane.YES_NO_OPTION,  | 
|---|
 | 628 | +                        JOptionPane.QUESTION_MESSAGE,  | 
|---|
 | 629 | +                        null,  | 
|---|
 | 630 | +                        options,  | 
|---|
 | 631 | +                        model.isReadOnly() ? "Save As" : "Save");  | 
|---|
 | 632 | +                Object result = options[n];  | 
|---|
 | 633 | +                if ("Discard Changes".equals(result))  | 
|---|
 | 634 | +                {  | 
|---|
 | 635 | +                        return true;  | 
|---|
 | 636 | +                }  | 
|---|
 | 637 | +                if ("Cancel".equals(result))  | 
|---|
 | 638 | +                {  | 
|---|
 | 639 | +                        return false;  | 
|---|
 | 640 | +                }  | 
|---|
 | 641 | +                if ("Save".equals(result))  | 
|---|
 | 642 | +                {  | 
|---|
 | 643 | +                        return save(model.getDbFile());  | 
|---|
 | 644 | +                }  | 
|---|
 | 645 | +  | 
|---|
 | 646 | +                // we are now at "save as..."  | 
|---|
 | 647 | +                return saveAs();  | 
|---|
 | 648 | +        }  | 
|---|
 | 649 | +  | 
|---|
 | 650 | +        public boolean saveAs()  | 
|---|
 | 651 | +        {  | 
|---|
 | 652 | +                File current = fileChooser.getSelectedFile();  | 
|---|
 | 653 | +                if (current != null)  | 
|---|
 | 654 | +                {  | 
|---|
 | 655 | +                        fileChooser.setSelectedFile(new File(current.getAbsolutePath() + " (copy)"));  | 
|---|
 | 656 | +                }  | 
|---|
 | 657 | +                int retval = fileChooser.showSaveDialog(this);  | 
|---|
 | 658 | +                if (retval == fileChooser.APPROVE_OPTION)  | 
|---|
 | 659 | +                {  | 
|---|
 | 660 | +                        String fileName = fileChooser.getSelectedFile().getAbsolutePath();  | 
|---|
 | 661 | +                        model.setReadOnly(false);  | 
|---|
 | 662 | +                        save.setEnabled(true);  | 
|---|
 | 663 | +                        return save(fileName);  | 
|---|
 | 664 | +                } else  | 
|---|
 | 665 | +                {  | 
|---|
 | 666 | +                        return false;  | 
|---|
 | 667 | +                }  | 
|---|
 | 668 | +        }  | 
|---|
 | 669 | +  | 
|---|
 | 670 | +        public void showUserError(Object o)  | 
|---|
 | 671 | +        {  | 
|---|
 | 672 | +                JOptionPane.showMessageDialog(this,  | 
|---|
 | 673 | +                        o,  | 
|---|
 | 674 | +                        "A problem occurred",  | 
|---|
 | 675 | +                        JOptionPane.ERROR_MESSAGE);  | 
|---|
 | 676 | +                if (o instanceof Exception)  | 
|---|
 | 677 | +                {  | 
|---|
 | 678 | +                        ((Exception) o).printStackTrace(System.out);  | 
|---|
 | 679 | +                }  | 
|---|
 | 680 | +        }  | 
|---|
 | 681 | +  | 
|---|
 | 682 | +        public void noteFileUse(String file)  | 
|---|
 | 683 | +        {  | 
|---|
 | 684 | +  | 
|---|
 | 685 | +                state.setCurrentFile(new File(file));  | 
|---|
 | 686 | +                state.save();  | 
|---|
 | 687 | +                makeRecentFileMenu();  | 
|---|
 | 688 | +  | 
|---|
 | 689 | +        }  | 
|---|
 | 690 | +  | 
|---|
 | 691 | +        public void clearFilters()  | 
|---|
 | 692 | +        {  | 
|---|
 | 693 | +                filterControlPanel.clearFilters();  | 
|---|
 | 694 | +        }  | 
|---|
 | 695 | +  | 
|---|
 | 696 | +        static String[] getVisibleFiles(String dir)  | 
|---|
 | 697 | +        {  | 
|---|
 | 698 | +                String[] s = new File(dir).list();  | 
|---|
 | 699 | +                ArrayList<String> real = new ArrayList<String>();  | 
|---|
 | 700 | +                for (int i = 0; i < s.length; i++)  | 
|---|
 | 701 | +                {  | 
|---|
 | 702 | +                        if (!s[i].startsWith("."))  | 
|---|
 | 703 | +                        {  | 
|---|
 | 704 | +                                real.add(s[i]);  | 
|---|
 | 705 | +                        }  | 
|---|
 | 706 | +                }  | 
|---|
 | 707 | +                return (String[]) real.toArray(new String[0]);  | 
|---|
 | 708 | +        }  | 
|---|
 | 709 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.model.*;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +import javax.swing.*;  | 
|---|
 | 6 | +import java.awt.event.*;  | 
|---|
 | 7 | +  | 
|---|
 | 8 | +// For some reason we have to do this in a separate class in order to  | 
|---|
 | 9 | +// get the menubar working right on the Mac.  | 
|---|
 | 10 | +  | 
|---|
 | 11 | +public class TimeflowAppLauncher {  | 
|---|
 | 12 | +	public static void main(String[] args) throws Exception  | 
|---|
 | 13 | +	{  | 
|---|
 | 14 | +		System.setProperty("apple.laf.useScreenMenuBar", "true");  | 
|---|
 | 15 | +		System.setProperty("com.apple.mrj.application.apple.menu.about.name", "TimeFlow");  | 
|---|
 | 16 | +		System.out.println("Running "+Display.version());  | 
|---|
 | 17 | +		  | 
|---|
 | 18 | +		try {  | 
|---|
 | 19 | +	        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());  | 
|---|
 | 20 | +	    }   | 
|---|
 | 21 | +	    catch (Exception e) {  | 
|---|
 | 22 | +	       System.out.println("Can't set system look & feel");  | 
|---|
 | 23 | +	    }		  | 
|---|
 | 24 | +		  | 
|---|
 | 25 | +		final TimeflowApp t=new TimeflowApp();  | 
|---|
 | 26 | +		t.splash=new AboutWindow(t, t.model.getDisplay());  | 
|---|
 | 27 | +		t.splash(true);  | 
|---|
 | 28 | +		SwingUtilities.invokeLater(new Runnable() {  | 
|---|
 | 29 | +			@Override  | 
|---|
 | 30 | +			public void run() {  | 
|---|
 | 31 | +				try  | 
|---|
 | 32 | +				{  | 
|---|
 | 33 | +					t.init();  | 
|---|
 | 34 | +					t.setVisible(true);				  | 
|---|
 | 35 | +				}  | 
|---|
 | 36 | +				catch (Exception e)  | 
|---|
 | 37 | +				{  | 
|---|
 | 38 | +					e.printStackTrace(System.out);  | 
|---|
 | 39 | +				}  | 
|---|
 | 40 | +				t.splash.addMouseListener(new MouseAdapter() {  | 
|---|
 | 41 | +  | 
|---|
 | 42 | +					@Override  | 
|---|
 | 43 | +					public void mouseClicked(MouseEvent e) {  | 
|---|
 | 44 | +						t.splash.setVisible(false);  | 
|---|
 | 45 | +					}}  | 
|---|
 | 46 | +				);  | 
|---|
 | 47 | +				t.splash(false);  | 
|---|
 | 48 | +				//t.splash.message=t.model.getDisplay().version();  | 
|---|
 | 49 | +			}});  | 
|---|
 | 50 | +		  | 
|---|
 | 51 | +	}  | 
|---|
 | 52 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.actions;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.model.*;  | 
|---|
 | 4 | +import timeflow.app.TimeflowApp;  | 
|---|
 | 5 | +import timeflow.app.ui.*;  | 
|---|
 | 6 | +import timeflow.data.db.*;  | 
|---|
 | 7 | +import timeflow.format.field.FieldFormatCatalog;  | 
|---|
 | 8 | +  | 
|---|
 | 9 | +import java.awt.event.*;  | 
|---|
 | 10 | +import javax.swing.*;  | 
|---|
 | 11 | +import java.util.*;  | 
|---|
 | 12 | +  | 
|---|
 | 13 | +public class AddFieldAction extends TimeflowAction {  | 
|---|
 | 14 | +  | 
|---|
 | 15 | +	public AddFieldAction(TimeflowApp app)  | 
|---|
 | 16 | +	{  | 
|---|
 | 17 | +		super(app, "Add Field...", null, "Add a field to this database");  | 
|---|
 | 18 | +	}  | 
|---|
 | 19 | +	  | 
|---|
 | 20 | +	@Override  | 
|---|
 | 21 | +	public void actionPerformed(ActionEvent e) {  | 
|---|
 | 22 | +		AddFieldPanel p=new AddFieldPanel();  | 
|---|
 | 23 | +		Object[] options = {"Cancel", "Add Field"};  | 
|---|
 | 24 | +		int n = JOptionPane.showOptionDialog(app,  | 
|---|
 | 25 | +				p,  | 
|---|
 | 26 | +				"Add New Field To Database",  | 
|---|
 | 27 | +				JOptionPane.YES_NO_CANCEL_OPTION,  | 
|---|
 | 28 | +				JOptionPane.QUESTION_MESSAGE,  | 
|---|
 | 29 | +				null,  | 
|---|
 | 30 | +				options,  | 
|---|
 | 31 | +				"Add Field");  | 
|---|
 | 32 | +		if (n==1)  | 
|---|
 | 33 | +		{  | 
|---|
 | 34 | +			String fieldName=p.name.getText();  | 
|---|
 | 35 | +			TFModel model=getModel();  | 
|---|
 | 36 | +			if (fieldName.trim().length()==0)  | 
|---|
 | 37 | +				app.showUserError("Field names can't be all spaces!");  | 
|---|
 | 38 | +			else if (model.getDB().getField(fieldName)!=null)  | 
|---|
 | 39 | +				app.showUserError("That name is already taken!");  | 
|---|
 | 40 | +			else  | 
|---|
 | 41 | +			{  | 
|---|
 | 42 | +				model.getDB().addField(fieldName, FieldFormatCatalog.javaClass((String)p.typeChoices.getSelectedItem()));  | 
|---|
 | 43 | +				model.noteAddField(this);  | 
|---|
 | 44 | +			}  | 
|---|
 | 45 | +		}  | 
|---|
 | 46 | +		else  | 
|---|
 | 47 | +			System.out.println("Canceled!");  | 
|---|
 | 48 | +	}  | 
|---|
 | 49 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.actions;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.model.*;  | 
|---|
 | 4 | +import timeflow.app.TimeflowApp;  | 
|---|
 | 5 | +import timeflow.app.ui.*;  | 
|---|
 | 6 | +import timeflow.data.db.*;  | 
|---|
 | 7 | +import timeflow.format.field.FieldFormatCatalog;  | 
|---|
 | 8 | +  | 
|---|
 | 9 | +import java.awt.Toolkit;  | 
|---|
 | 10 | +import java.awt.event.*;  | 
|---|
 | 11 | +import javax.swing.*;  | 
|---|
 | 12 | +import java.util.*;  | 
|---|
 | 13 | +  | 
|---|
 | 14 | +public class AddRecordAction extends TimeflowAction {  | 
|---|
 | 15 | +  | 
|---|
 | 16 | +	public AddRecordAction(TimeflowApp app)  | 
|---|
 | 17 | +	{  | 
|---|
 | 18 | +		super(app, "Add Record...", null, "Add a record to this database");  | 
|---|
 | 19 | +		accelerate('A');  | 
|---|
 | 20 | +	}  | 
|---|
 | 21 | +	  | 
|---|
 | 22 | +	@Override  | 
|---|
 | 23 | +	public void actionPerformed(ActionEvent e) {  | 
|---|
 | 24 | +		EditRecordPanel.add(getModel());  | 
|---|
 | 25 | +	}  | 
|---|
 | 26 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.actions;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.model.*;  | 
|---|
 | 4 | +import timeflow.app.TimeflowApp;  | 
|---|
 | 5 | +import timeflow.app.ui.*;  | 
|---|
 | 6 | +import timeflow.data.db.*;  | 
|---|
 | 7 | +import timeflow.format.field.FieldFormatCatalog;  | 
|---|
 | 8 | +  | 
|---|
 | 9 | +import java.awt.event.*;  | 
|---|
 | 10 | +import javax.swing.*;  | 
|---|
 | 11 | +import java.util.*;  | 
|---|
 | 12 | +  | 
|---|
 | 13 | +public class CopySchemaAction extends TimeflowAction {  | 
|---|
 | 14 | +  | 
|---|
 | 15 | +	public CopySchemaAction(TimeflowApp app)  | 
|---|
 | 16 | +	{  | 
|---|
 | 17 | +		super(app, "New With Same Fields", null,   | 
|---|
 | 18 | +				"Create a new, blank database with same fields as the current one.");  | 
|---|
 | 19 | +	}  | 
|---|
 | 20 | +	  | 
|---|
 | 21 | +	public void actionPerformed(ActionEvent e)   | 
|---|
 | 22 | +	{  | 
|---|
 | 23 | +		java.util.List<Field> fields=getModel().getDB().getFields();  | 
|---|
 | 24 | +		ActDB db=new BasicDB("Unspecified");  | 
|---|
 | 25 | +		for (Field f: fields)  | 
|---|
 | 26 | +			db.addField(f.getName(), f.getType());  | 
|---|
 | 27 | +		getModel().setDB(db, "[new data]", true, this);  | 
|---|
 | 28 | +	}  | 
|---|
 | 29 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.actions;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.model.*;  | 
|---|
 | 4 | +import timeflow.app.TimeflowApp;  | 
|---|
 | 5 | +import timeflow.app.ui.*;  | 
|---|
 | 6 | +import timeflow.data.db.*;  | 
|---|
 | 7 | +import timeflow.format.field.FieldFormatCatalog;  | 
|---|
 | 8 | +  | 
|---|
 | 9 | +import java.awt.event.*;  | 
|---|
 | 10 | +import javax.swing.*;  | 
|---|
 | 11 | +import java.util.*;  | 
|---|
 | 12 | +  | 
|---|
 | 13 | +public class DateFieldAction extends TimeflowAction {  | 
|---|
 | 14 | +  | 
|---|
 | 15 | +	public DateFieldAction(TimeflowApp app)  | 
|---|
 | 16 | +	{  | 
|---|
 | 17 | +		super(app, "Set Date Fields...", null, "Set date fields corresponding to start, end.");  | 
|---|
 | 18 | +	}  | 
|---|
 | 19 | +	  | 
|---|
 | 20 | +	@Override  | 
|---|
 | 21 | +	public void actionPerformed(ActionEvent e) {  | 
|---|
 | 22 | +		DateFieldPanel.popWindow(app.model);  | 
|---|
 | 23 | +	}  | 
|---|
 | 24 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.actions;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.model.*;  | 
|---|
 | 4 | +import timeflow.app.TimeflowApp;  | 
|---|
 | 5 | +import timeflow.app.ui.*;  | 
|---|
 | 6 | +import timeflow.data.db.*;  | 
|---|
 | 7 | +  | 
|---|
 | 8 | +import java.awt.event.*;  | 
|---|
 | 9 | +import javax.swing.*;  | 
|---|
 | 10 | +import java.util.*;  | 
|---|
 | 11 | +  | 
|---|
 | 12 | +public class DeleteFieldAction extends TimeflowAction {  | 
|---|
 | 13 | +  | 
|---|
 | 14 | +	public DeleteFieldAction(TimeflowApp app)  | 
|---|
 | 15 | +	{  | 
|---|
 | 16 | +		super(app, "Delete Field...", null, "Delete a field from this database");  | 
|---|
 | 17 | +	}  | 
|---|
 | 18 | +	  | 
|---|
 | 19 | +	@Override  | 
|---|
 | 20 | +	public void actionPerformed(ActionEvent e) {  | 
|---|
 | 21 | +		ArrayList<String> options=new ArrayList<String>();  | 
|---|
 | 22 | +		for (Field f: getModel().getDB().getFields())  | 
|---|
 | 23 | +			options.add(f.getName());  | 
|---|
 | 24 | +		String[] o=(String[])options.toArray(new String[0]);  | 
|---|
 | 25 | +		String fieldToDelete = (String)JOptionPane.showInputDialog(  | 
|---|
 | 26 | +		                    app,  | 
|---|
 | 27 | +		                    "Field to delete:",  | 
|---|
 | 28 | +		                    "Delete Field",  | 
|---|
 | 29 | +		                    JOptionPane.PLAIN_MESSAGE,  | 
|---|
 | 30 | +		                    null,  | 
|---|
 | 31 | +		                    o,  | 
|---|
 | 32 | +		                    o[0]);  | 
|---|
 | 33 | +  | 
|---|
 | 34 | +		if (fieldToDelete!=null)  | 
|---|
 | 35 | +		{  | 
|---|
 | 36 | +			TFModel model=getModel();  | 
|---|
 | 37 | +			Field f=model.getDB().getField(fieldToDelete);  | 
|---|
 | 38 | +			model.getDB().deleteField(f);			  | 
|---|
 | 39 | +			model.noteSchemaChange(this);  | 
|---|
 | 40 | +			return;  | 
|---|
 | 41 | +		}	  | 
|---|
 | 42 | +	}  | 
|---|
 | 43 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.actions;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.model.*;  | 
|---|
 | 4 | +import timeflow.app.ui.*;  | 
|---|
 | 5 | +import timeflow.app.*;  | 
|---|
 | 6 | +import timeflow.data.db.*;  | 
|---|
 | 7 | +  | 
|---|
 | 8 | +import java.awt.event.*;  | 
|---|
 | 9 | +import javax.swing.*;  | 
|---|
 | 10 | +import java.util.*;  | 
|---|
 | 11 | +  | 
|---|
 | 12 | +  | 
|---|
 | 13 | +public class DeleteSelectedAction extends TimeflowAction {  | 
|---|
 | 14 | +  | 
|---|
 | 15 | +	public DeleteSelectedAction(TimeflowApp app)  | 
|---|
 | 16 | +	{  | 
|---|
 | 17 | +		super(app, "Delete Selected Items...", null, "Delete the currently visible events");  | 
|---|
 | 18 | +	}  | 
|---|
 | 19 | +	  | 
|---|
 | 20 | +	@Override  | 
|---|
 | 21 | +	public void actionPerformed(ActionEvent e) {  | 
|---|
 | 22 | +		  | 
|---|
 | 23 | +		HashSet<Act> keepers=new HashSet<Act>(); // switching between sets and lists  | 
|---|
 | 24 | +		keepers.addAll(getModel().getDB().all()); // for efficiency. maybe silly?  | 
|---|
 | 25 | +		ActList selected=getModel().getActs();  | 
|---|
 | 26 | +		for (Act a: selected)  | 
|---|
 | 27 | +			keepers.remove(a);  | 
|---|
 | 28 | +		ActList keepList=new ActList(getModel().getDB());  | 
|---|
 | 29 | +		keepList.addAll(keepers);  | 
|---|
 | 30 | +		  | 
|---|
 | 31 | +		MassDeletePanel panel=new MassDeletePanel(getModel(), keepList,   | 
|---|
 | 32 | +				"Delete all selected items.");  | 
|---|
 | 33 | +		Object[] options = {"Cancel", "Proceed"};  | 
|---|
 | 34 | +		int n = JOptionPane.showOptionDialog(app,  | 
|---|
 | 35 | +					panel,  | 
|---|
 | 36 | +					"Delete Selected",  | 
|---|
 | 37 | +					JOptionPane.YES_NO_CANCEL_OPTION,  | 
|---|
 | 38 | +					JOptionPane.PLAIN_MESSAGE,  | 
|---|
 | 39 | +					null,  | 
|---|
 | 40 | +					options,  | 
|---|
 | 41 | +					"Proceed");  | 
|---|
 | 42 | +		panel.detachFromModel();  | 
|---|
 | 43 | +		if (n==1)  | 
|---|
 | 44 | +		{  | 
|---|
 | 45 | +			panel.applyAction();  | 
|---|
 | 46 | +			app.clearFilters();  | 
|---|
 | 47 | +			getModel().noteSchemaChange(this);  | 
|---|
 | 48 | +		}  | 
|---|
 | 49 | +	}  | 
|---|
 | 50 | +  | 
|---|
 | 51 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.actions;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.model.*;  | 
|---|
 | 4 | +import timeflow.app.ui.*;  | 
|---|
 | 5 | +import timeflow.app.*;  | 
|---|
 | 6 | +  | 
|---|
 | 7 | +import java.awt.event.*;  | 
|---|
 | 8 | +import javax.swing.*;  | 
|---|
 | 9 | +  | 
|---|
 | 10 | +  | 
|---|
 | 11 | +public class DeleteUnselectedAction extends TimeflowAction {  | 
|---|
 | 12 | +  | 
|---|
 | 13 | +	public DeleteUnselectedAction(TimeflowApp app)  | 
|---|
 | 14 | +	{  | 
|---|
 | 15 | +		super(app, "Delete Unselected Items...", null, "Delete all but the currently visible events");  | 
|---|
 | 16 | +	}  | 
|---|
 | 17 | +	  | 
|---|
 | 18 | +	@Override  | 
|---|
 | 19 | +	public void actionPerformed(ActionEvent e) {  | 
|---|
 | 20 | +		MassDeletePanel panel=new MassDeletePanel(getModel(), getModel().getActs(),   | 
|---|
 | 21 | +				"Delete unselected items.");  | 
|---|
 | 22 | +		Object[] options = {"Cancel", "Proceed"};  | 
|---|
 | 23 | +		int n = JOptionPane.showOptionDialog(app,  | 
|---|
 | 24 | +					panel,  | 
|---|
 | 25 | +					"Delete Unselected",  | 
|---|
 | 26 | +					JOptionPane.YES_NO_CANCEL_OPTION,  | 
|---|
 | 27 | +					JOptionPane.PLAIN_MESSAGE,  | 
|---|
 | 28 | +					null,  | 
|---|
 | 29 | +					options,  | 
|---|
 | 30 | +					"Proceed");  | 
|---|
 | 31 | +		panel.detachFromModel();  | 
|---|
 | 32 | +		if (n==1)  | 
|---|
 | 33 | +		{  | 
|---|
 | 34 | +			panel.applyAction();  | 
|---|
 | 35 | +			app.clearFilters();  | 
|---|
 | 36 | +			getModel().noteSchemaChange(this);  | 
|---|
 | 37 | +		}  | 
|---|
 | 38 | +	}  | 
|---|
 | 39 | +  | 
|---|
 | 40 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.actions;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.model.*;  | 
|---|
 | 4 | +import timeflow.app.TimeflowApp;  | 
|---|
 | 5 | +import timeflow.app.ui.*;  | 
|---|
 | 6 | +  | 
|---|
 | 7 | +import java.awt.event.*;  | 
|---|
 | 8 | +import javax.swing.*;  | 
|---|
 | 9 | +  | 
|---|
 | 10 | +  | 
|---|
 | 11 | +public class EditSourceAction extends TimeflowAction {  | 
|---|
 | 12 | +  | 
|---|
 | 13 | +	public EditSourceAction(TimeflowApp app)  | 
|---|
 | 14 | +	{  | 
|---|
 | 15 | +		super(app, "Edit Source/Credit Line...", null, "Edit credit line for this database");  | 
|---|
 | 16 | +	}  | 
|---|
 | 17 | +	  | 
|---|
 | 18 | +	@Override  | 
|---|
 | 19 | +	public void actionPerformed(ActionEvent e) {  | 
|---|
 | 20 | +		TFModel model=getModel();  | 
|---|
 | 21 | +		String source = (String)JOptionPane.showInputDialog(  | 
|---|
 | 22 | +                app,  | 
|---|
 | 23 | +                null,  | 
|---|
 | 24 | +                "Edit Source/Credit Line",  | 
|---|
 | 25 | +                JOptionPane.PLAIN_MESSAGE,  | 
|---|
 | 26 | +                null,  | 
|---|
 | 27 | +                null,  | 
|---|
 | 28 | +                model.getDB().getSource());  | 
|---|
 | 29 | +  | 
|---|
 | 30 | +		if (source!=null) {  | 
|---|
 | 31 | +		model.getDB().setSource(source);  | 
|---|
 | 32 | +		model.noteNewSource(this);  | 
|---|
 | 33 | +		return;  | 
|---|
 | 34 | +		}  | 
|---|
 | 35 | +	}  | 
|---|
 | 36 | +  | 
|---|
 | 37 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.actions;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.model.*;  | 
|---|
 | 4 | +import timeflow.app.TimeflowApp;  | 
|---|
 | 5 | +import timeflow.app.ui.*;  | 
|---|
 | 6 | +import timeflow.data.db.*;  | 
|---|
 | 7 | +import timeflow.format.field.FieldFormatCatalog;  | 
|---|
 | 8 | +import timeflow.format.file.DelimitedFormat;  | 
|---|
 | 9 | +  | 
|---|
 | 10 | +import java.awt.event.*;  | 
|---|
 | 11 | +import javax.swing.*;  | 
|---|
 | 12 | +import java.util.*;  | 
|---|
 | 13 | +  | 
|---|
 | 14 | +public class ImportFromPasteAction extends TimeflowAction {  | 
|---|
 | 15 | +  | 
|---|
 | 16 | +	public ImportFromPasteAction(TimeflowApp app)  | 
|---|
 | 17 | +	{  | 
|---|
 | 18 | +		super(app, "Paste From Spreadsheet / HTML...", null, "Import from copy-and-pasted data.");  | 
|---|
 | 19 | +	}  | 
|---|
 | 20 | +	  | 
|---|
 | 21 | +	public void actionPerformed(ActionEvent event)   | 
|---|
 | 22 | +	{  | 
|---|
 | 23 | +		if (!app.checkSaveStatus())  | 
|---|
 | 24 | +			return;  | 
|---|
 | 25 | +		JTextArea text=new JTextArea(10,40);  | 
|---|
 | 26 | +		JScrollPane scroll=new JScrollPane(text);  | 
|---|
 | 27 | +		text.setText("Paste here! (replacing this :-)");  | 
|---|
 | 28 | +		text.setSelectionStart(0);  | 
|---|
 | 29 | +		text.setSelectionEnd(text.getText().length());  | 
|---|
 | 30 | +		Object[] options = {"Cancel", "Import"};  | 
|---|
 | 31 | +		int n = JOptionPane.showOptionDialog(app,  | 
|---|
 | 32 | +					scroll,  | 
|---|
 | 33 | +					"Import From Paste",  | 
|---|
 | 34 | +					JOptionPane.YES_NO_CANCEL_OPTION,  | 
|---|
 | 35 | +					JOptionPane.QUESTION_MESSAGE,  | 
|---|
 | 36 | +					null,  | 
|---|
 | 37 | +					options,  | 
|---|
 | 38 | +					"Import");  | 
|---|
 | 39 | +		if (n==1)  | 
|---|
 | 40 | +		{  | 
|---|
 | 41 | +			try  | 
|---|
 | 42 | +			{  | 
|---|
 | 43 | +				String pasted=text.getText();  | 
|---|
 | 44 | +				String[][] data=DelimitedFormat.readArrayFromString(pasted, System.out);  | 
|---|
 | 45 | +				app.showImportEditor("Paste", data);  | 
|---|
 | 46 | +			}  | 
|---|
 | 47 | +			catch (Exception e)  | 
|---|
 | 48 | +			{  | 
|---|
 | 49 | +				app.showUserError(e);  | 
|---|
 | 50 | +			}  | 
|---|
 | 51 | +		}  | 
|---|
 | 52 | +	}  | 
|---|
 | 53 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.actions;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.model.*;  | 
|---|
 | 4 | +import timeflow.app.TimeflowApp;  | 
|---|
 | 5 | +import timeflow.app.ui.*;  | 
|---|
 | 6 | +import timeflow.data.db.*;  | 
|---|
 | 7 | +import timeflow.format.field.FieldFormatCatalog;  | 
|---|
 | 8 | +  | 
|---|
 | 9 | +import java.awt.event.*;  | 
|---|
 | 10 | +import javax.swing.*;  | 
|---|
 | 11 | +import java.util.*;  | 
|---|
 | 12 | +  | 
|---|
 | 13 | +public class NewDataAction extends TimeflowAction {  | 
|---|
 | 14 | +  | 
|---|
 | 15 | +	public NewDataAction(TimeflowApp app)  | 
|---|
 | 16 | +	{  | 
|---|
 | 17 | +		super(app, "New", null, "Create a new, blank database");  | 
|---|
 | 18 | +		accelerate('N');  | 
|---|
 | 19 | +  | 
|---|
 | 20 | +	}  | 
|---|
 | 21 | +	  | 
|---|
 | 22 | +	public void actionPerformed(ActionEvent e)   | 
|---|
 | 23 | +	{  | 
|---|
 | 24 | +		if (app.checkSaveStatus())  | 
|---|
 | 25 | +			getModel().setDB(new BasicDB("Unspecified"), "[new data]", true, this);  | 
|---|
 | 26 | +	}  | 
|---|
 | 27 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.actions;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.model.*;  | 
|---|
 | 4 | +import timeflow.app.TimeflowApp;  | 
|---|
 | 5 | +import timeflow.app.ui.*;  | 
|---|
 | 6 | +import timeflow.data.db.*;  | 
|---|
 | 7 | +import timeflow.format.field.FieldFormatCatalog;  | 
|---|
 | 8 | +  | 
|---|
 | 9 | +import java.awt.event.*;  | 
|---|
 | 10 | +import javax.swing.*;  | 
|---|
 | 11 | +import java.util.*;  | 
|---|
 | 12 | +  | 
|---|
 | 13 | +public class QuitAction extends TimeflowAction {  | 
|---|
 | 14 | +  | 
|---|
 | 15 | +	public QuitAction(TimeflowApp app, TFModel model)  | 
|---|
 | 16 | +	{  | 
|---|
 | 17 | +		super(app, "Quit", null, "Quit the program");  | 
|---|
 | 18 | +	}  | 
|---|
 | 19 | +	  | 
|---|
 | 20 | +	@Override  | 
|---|
 | 21 | +	public void actionPerformed(ActionEvent e) {  | 
|---|
 | 22 | +		quit();  | 
|---|
 | 23 | +	}  | 
|---|
 | 24 | +	  | 
|---|
 | 25 | +	public void quit()  | 
|---|
 | 26 | +	{  | 
|---|
 | 27 | +		if (app.checkSaveStatus())  | 
|---|
 | 28 | +		{  | 
|---|
 | 29 | +			System.exit(0);  | 
|---|
 | 30 | +		}  | 
|---|
 | 31 | +	}  | 
|---|
 | 32 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.actions;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.model.*;  | 
|---|
 | 4 | +import timeflow.app.ui.*;  | 
|---|
 | 5 | +import timeflow.app.*;  | 
|---|
 | 6 | +import timeflow.data.db.*;  | 
|---|
 | 7 | +  | 
|---|
 | 8 | +import java.awt.*;  | 
|---|
 | 9 | +import java.awt.event.*;  | 
|---|
 | 10 | +  | 
|---|
 | 11 | +import javax.swing.*;  | 
|---|
 | 12 | +  | 
|---|
 | 13 | +import java.util.*;  | 
|---|
 | 14 | +  | 
|---|
 | 15 | +public class RenameFieldAction extends TimeflowAction {  | 
|---|
 | 16 | +  | 
|---|
 | 17 | +	public RenameFieldAction(TimeflowApp app)  | 
|---|
 | 18 | +	{  | 
|---|
 | 19 | +		super(app, "Rename Field...", null, "Rename a field from this database");  | 
|---|
 | 20 | +	}  | 
|---|
 | 21 | +	  | 
|---|
 | 22 | +	@Override  | 
|---|
 | 23 | +	public void actionPerformed(ActionEvent e) {  | 
|---|
 | 24 | +		JPanel panel=new JPanel();  | 
|---|
 | 25 | +		panel.setLayout(new GridLayout(4,1));  | 
|---|
 | 26 | +		panel.add(new JLabel("Choose a field and type a new name."));  | 
|---|
 | 27 | +		final JComboBox fieldChoices=new JComboBox();  | 
|---|
 | 28 | +		panel.add(fieldChoices);  | 
|---|
 | 29 | +		ArrayList<String> options=new ArrayList<String>();  | 
|---|
 | 30 | +		for (Field f: getModel().getDB().getFields())  | 
|---|
 | 31 | +			fieldChoices.addItem(f.getName());  | 
|---|
 | 32 | +		JPanel inputPanel=new JPanel();  | 
|---|
 | 33 | +		inputPanel.setLayout(new FlowLayout(FlowLayout.LEFT));  | 
|---|
 | 34 | +		inputPanel.add(new JLabel("New Name:"));  | 
|---|
 | 35 | +		final JTextField nameField=new JTextField(20);  | 
|---|
 | 36 | +		inputPanel.add(nameField);  | 
|---|
 | 37 | +		nameField.requestFocus();  | 
|---|
 | 38 | +		final JLabel feedback=new JLabel("(No name entered)");  | 
|---|
 | 39 | +  | 
|---|
 | 40 | +		nameField.addKeyListener(new KeyListener() {  | 
|---|
 | 41 | +			@Override  | 
|---|
 | 42 | +			public void keyPressed(KeyEvent e) {  | 
|---|
 | 43 | +				// TODO Auto-generated method stub  | 
|---|
 | 44 | +				  | 
|---|
 | 45 | +			}  | 
|---|
 | 46 | +  | 
|---|
 | 47 | +			@Override  | 
|---|
 | 48 | +			public void keyReleased(KeyEvent e) {  | 
|---|
 | 49 | +				String name=nameField.getText();  | 
|---|
 | 50 | +				Field other=getModel().getDB().getField(name);  | 
|---|
 | 51 | +				//System.out.println("name="+name);  | 
|---|
 | 52 | +				if (name.trim().length()==0)  | 
|---|
 | 53 | +				{  | 
|---|
 | 54 | +					feedback.setText("(No name entered)");  | 
|---|
 | 55 | +				} else if (other!=null && !other.getName().equals(fieldChoices.getSelectedItem()))  | 
|---|
 | 56 | +				{  | 
|---|
 | 57 | +					feedback.setText("A field named '"+name+"' already exists.");  | 
|---|
 | 58 | +				} else  | 
|---|
 | 59 | +					feedback.setText("");  | 
|---|
 | 60 | +			}  | 
|---|
 | 61 | +  | 
|---|
 | 62 | +			@Override  | 
|---|
 | 63 | +			public void keyTyped(KeyEvent e) {  | 
|---|
 | 64 | +				// TODO Auto-generated method stub  | 
|---|
 | 65 | +				  | 
|---|
 | 66 | +			}});  | 
|---|
 | 67 | +		  | 
|---|
 | 68 | +		panel.add(inputPanel);  | 
|---|
 | 69 | +		feedback.setForeground(Color.gray);  | 
|---|
 | 70 | +		panel.add(feedback);  | 
|---|
 | 71 | +		  | 
|---|
 | 72 | +		String[] o={"OK", "Cancel"};  | 
|---|
 | 73 | +		int n = JOptionPane.showOptionDialog(  | 
|---|
 | 74 | +				app,  | 
|---|
 | 75 | +				panel,  | 
|---|
 | 76 | +				"Rename Field",  | 
|---|
 | 77 | +				JOptionPane.YES_NO_CANCEL_OPTION,  | 
|---|
 | 78 | +				JOptionPane.PLAIN_MESSAGE,  | 
|---|
 | 79 | +				null,  | 
|---|
 | 80 | +				o,  | 
|---|
 | 81 | +				o[0]);  | 
|---|
 | 82 | +  | 
|---|
 | 83 | +		if (n==0)  | 
|---|
 | 84 | +		{  | 
|---|
 | 85 | +			Field old=getModel().getDB().getField((String)fieldChoices.getSelectedItem());  | 
|---|
 | 86 | +			String newName=nameField.getText();  | 
|---|
 | 87 | +			Field conflict=getModel().getDB().getField(newName);  | 
|---|
 | 88 | +			boolean tooSpacey=newName.trim().length()==0;  | 
|---|
 | 89 | +			if (tooSpacey)  | 
|---|
 | 90 | +				app.showUserError("Can't change the field name to be empty.");  | 
|---|
 | 91 | +			else if (conflict!=null && conflict!=old)  | 
|---|
 | 92 | +				app.showUserError("A field named '"+newName+"' already exists.");  | 
|---|
 | 93 | +			else  | 
|---|
 | 94 | +			{  | 
|---|
 | 95 | +				getModel().getDB().renameField(old, nameField.getText());  | 
|---|
 | 96 | +				getModel().noteSchemaChange(this);  | 
|---|
 | 97 | +			}  | 
|---|
 | 98 | +		}  | 
|---|
 | 99 | +	}  | 
|---|
 | 100 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.actions;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.model.*;  | 
|---|
 | 4 | +import timeflow.app.ui.*;  | 
|---|
 | 5 | +import timeflow.app.*;  | 
|---|
 | 6 | +  | 
|---|
 | 7 | +import java.awt.event.*;  | 
|---|
 | 8 | +import javax.swing.*;  | 
|---|
 | 9 | +  | 
|---|
 | 10 | +  | 
|---|
 | 11 | +public class ReorderFieldsAction extends TimeflowAction {  | 
|---|
 | 12 | +  | 
|---|
 | 13 | +	public ReorderFieldsAction(TimeflowApp app)  | 
|---|
 | 14 | +	{  | 
|---|
 | 15 | +		super(app, "Reorder Fields...", null, "Edit the order of fields");  | 
|---|
 | 16 | +	}  | 
|---|
 | 17 | +	  | 
|---|
 | 18 | +	@Override  | 
|---|
 | 19 | +	public void actionPerformed(ActionEvent e) {  | 
|---|
 | 20 | +		ReorderFieldsPanel panel=new ReorderFieldsPanel(getModel());  | 
|---|
 | 21 | +		Object[] options = {"Cancel", "Apply"};  | 
|---|
 | 22 | +		int n = JOptionPane.showOptionDialog(app,  | 
|---|
 | 23 | +					panel,  | 
|---|
 | 24 | +					"Reorder Fields",  | 
|---|
 | 25 | +					JOptionPane.YES_NO_CANCEL_OPTION,  | 
|---|
 | 26 | +					JOptionPane.PLAIN_MESSAGE,  | 
|---|
 | 27 | +					null,  | 
|---|
 | 28 | +					options,  | 
|---|
 | 29 | +					"Apply");  | 
|---|
 | 30 | +		panel.detachFromModel();  | 
|---|
 | 31 | +		if (n==1)  | 
|---|
 | 32 | +		{  | 
|---|
 | 33 | +			panel.applyReordering();  | 
|---|
 | 34 | +			getModel().noteSchemaChange(this);  | 
|---|
 | 35 | +		}  | 
|---|
 | 36 | +	}  | 
|---|
 | 37 | +  | 
|---|
 | 38 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.actions;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.model.*;  | 
|---|
 | 4 | +import timeflow.app.*;  | 
|---|
 | 5 | +import timeflow.format.file.*;  | 
|---|
 | 6 | +  | 
|---|
 | 7 | +import javax.swing.*;  | 
|---|
 | 8 | +  | 
|---|
 | 9 | +import java.awt.Toolkit;  | 
|---|
 | 10 | +import java.io.*;  | 
|---|
 | 11 | +  | 
|---|
 | 12 | +public abstract class TimeflowAction extends AbstractAction {  | 
|---|
 | 13 | +	TimeflowApp app;  | 
|---|
 | 14 | +  | 
|---|
 | 15 | +    public TimeflowAction(TimeflowApp app, String text, ImageIcon icon, String desc)   | 
|---|
 | 16 | +    {  | 
|---|
 | 17 | +		super(text, icon);  | 
|---|
 | 18 | +		this.app=app;  | 
|---|
 | 19 | +		putValue(SHORT_DESCRIPTION, desc);  | 
|---|
 | 20 | +	}  | 
|---|
 | 21 | +      | 
|---|
 | 22 | +	  | 
|---|
 | 23 | +	protected void accelerate(char c)  | 
|---|
 | 24 | +	{  | 
|---|
 | 25 | +		putValue(Action.ACCELERATOR_KEY,KeyStroke.getKeyStroke(c,  | 
|---|
 | 26 | +			    Toolkit.getDefaultToolkit(  ).getMenuShortcutKeyMask(  ), false));  | 
|---|
 | 27 | +	}  | 
|---|
 | 28 | +  | 
|---|
 | 29 | +  | 
|---|
 | 30 | +    protected TFModel getModel()  | 
|---|
 | 31 | +    {  | 
|---|
 | 32 | +    	return app.model;  | 
|---|
 | 33 | +    }  | 
|---|
 | 34 | +      | 
|---|
 | 35 | +  | 
|---|
 | 36 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.actions;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import java.awt.Toolkit;  | 
|---|
 | 4 | +import java.awt.event.ActionEvent;  | 
|---|
 | 5 | +  | 
|---|
 | 6 | +import javax.swing.Action;  | 
|---|
 | 7 | +import javax.swing.JOptionPane;  | 
|---|
 | 8 | +import javax.swing.KeyStroke;  | 
|---|
 | 9 | +  | 
|---|
 | 10 | +import timeflow.app.TimeflowApp;  | 
|---|
 | 11 | +import timeflow.app.ui.ReorderFieldsPanel;  | 
|---|
 | 12 | +import timeflow.model.Display;  | 
|---|
 | 13 | +  | 
|---|
 | 14 | +public class WebDocAction extends TimeflowAction {  | 
|---|
 | 15 | +	public WebDocAction(TimeflowApp app)  | 
|---|
 | 16 | +	{  | 
|---|
 | 17 | +		super(app, "Documentation & License Info...", null, "Read web documentation.");  | 
|---|
 | 18 | +	}  | 
|---|
 | 19 | +	  | 
|---|
 | 20 | +	@Override  | 
|---|
 | 21 | +	public void actionPerformed(ActionEvent e) {  | 
|---|
 | 22 | +		Display.launchBrowser("http://wiki.github.com/FlowingMedia/TimeFlow/");  | 
|---|
 | 23 | +	}  | 
|---|
 | 24 | +  | 
|---|
 | 25 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.ui;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.app.*;  | 
|---|
 | 4 | +import timeflow.format.field.FieldFormatCatalog;  | 
|---|
 | 5 | +  | 
|---|
 | 6 | +import javax.swing.*;  | 
|---|
 | 7 | +import java.awt.*;  | 
|---|
 | 8 | +import java.awt.event.*;  | 
|---|
 | 9 | +  | 
|---|
 | 10 | +  | 
|---|
 | 11 | +public class AddFieldPanel extends JPanel {  | 
|---|
 | 12 | +	public JTextField name=new JTextField(12);  | 
|---|
 | 13 | +	public JComboBox typeChoices=new JComboBox();  | 
|---|
 | 14 | +	public AddFieldPanel()  | 
|---|
 | 15 | +	{  | 
|---|
 | 16 | +		for (String choice: FieldFormatCatalog.classNames())  | 
|---|
 | 17 | +			typeChoices.addItem(choice);  | 
|---|
 | 18 | +		setLayout(new GridLayout(2,2));  | 
|---|
 | 19 | +		add(new JLabel("Field Name"));  | 
|---|
 | 20 | +		add(name);  | 
|---|
 | 21 | +		add(new JLabel("Field Type"));  | 
|---|
 | 22 | +		add(typeChoices);  | 
|---|
 | 23 | +	}  | 
|---|
 | 24 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.ui;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.model.*;  | 
|---|
 | 4 | +import timeflow.app.ui.filter.FilterCategoryPanel;  | 
|---|
 | 5 | +import timeflow.data.db.*;  | 
|---|
 | 6 | +import timeflow.data.db.filter.FieldValueFilter;  | 
|---|
 | 7 | +import timeflow.data.db.filter.ValueFilter;  | 
|---|
 | 8 | +import timeflow.data.time.*;  | 
|---|
 | 9 | +  | 
|---|
 | 10 | +import timeflow.util.*;  | 
|---|
 | 11 | +  | 
|---|
 | 12 | +import java.awt.*;  | 
|---|
 | 13 | +  | 
|---|
 | 14 | +import javax.swing.JLabel;  | 
|---|
 | 15 | +import javax.swing.event.ListSelectionEvent;  | 
|---|
 | 16 | +import javax.swing.event.ListSelectionListener;  | 
|---|
 | 17 | +  | 
|---|
 | 18 | +public class ColorLegendPanel extends ModelPanel {  | 
|---|
 | 19 | +	  | 
|---|
 | 20 | +  | 
|---|
 | 21 | +	Field oldColor;  | 
|---|
 | 22 | +  | 
|---|
 | 23 | +	public ColorLegendPanel(TFModel model)  | 
|---|
 | 24 | +	{  | 
|---|
 | 25 | +		super(model);  | 
|---|
 | 26 | +		setBackground(Color.white);  | 
|---|
 | 27 | +		setLayout(new GridLayout(1,1));  | 
|---|
 | 28 | +	}  | 
|---|
 | 29 | +	  | 
|---|
 | 30 | +	@Override  | 
|---|
 | 31 | +	public void note(TFEvent e) {  | 
|---|
 | 32 | +		Field color=getModel().getColorField();  | 
|---|
 | 33 | +		if (color!=null && color!=oldColor)  | 
|---|
 | 34 | +		{  | 
|---|
 | 35 | +				removeAll();  | 
|---|
 | 36 | +				final FilterCategoryPanel p=new FilterCategoryPanel("Color Legend: '"+color.getName()+"'",   | 
|---|
 | 37 | +						color, this);  | 
|---|
 | 38 | +				add(p);  | 
|---|
 | 39 | +				Bag<String> data=DBUtils.countValues(getModel().getDB().all(), color);  | 
|---|
 | 40 | +				p.setData(data);  | 
|---|
 | 41 | +				p.dataList.addListSelectionListener(new ListSelectionListener() {				  | 
|---|
 | 42 | +					@Override  | 
|---|
 | 43 | +					public void valueChanged(ListSelectionEvent e) {  | 
|---|
 | 44 | +						ValueFilter f=(ValueFilter)p.defineFilter();	  | 
|---|
 | 45 | +						getModel().setGrayFilter(f, this);  | 
|---|
 | 46 | +					}  | 
|---|
 | 47 | +				});  | 
|---|
 | 48 | +				  | 
|---|
 | 49 | +				oldColor=color;  | 
|---|
 | 50 | +				revalidate();  | 
|---|
 | 51 | +				return;  | 
|---|
 | 52 | +		} else if (color==null)  | 
|---|
 | 53 | +		{  | 
|---|
 | 54 | +			removeAll();  | 
|---|
 | 55 | +		}  | 
|---|
 | 56 | +		repaint();  | 
|---|
 | 57 | +	}  | 
|---|
 | 58 | +	  | 
|---|
 | 59 | +	public Dimension getPreferredSize()  | 
|---|
 | 60 | +	{  | 
|---|
 | 61 | +		return new Dimension(200,400);  | 
|---|
 | 62 | +	}  | 
|---|
 | 63 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.ui;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import javax.swing.*;  | 
|---|
 | 4 | +import java.awt.*;  | 
|---|
 | 5 | +  | 
|---|
 | 6 | +public class ComponentCluster extends JPanel  | 
|---|
 | 7 | +{  | 
|---|
 | 8 | +	int numComps=0;  | 
|---|
 | 9 | +	int x1=80;  | 
|---|
 | 10 | +	int width=200;  | 
|---|
 | 11 | +	int compH=30;  | 
|---|
 | 12 | +	DottedLine line=new DottedLine();  | 
|---|
 | 13 | +	  | 
|---|
 | 14 | +	public ComponentCluster(String name)  | 
|---|
 | 15 | +	{  | 
|---|
 | 16 | +		setBackground(Color.white);  | 
|---|
 | 17 | +		setLayout(null);  | 
|---|
 | 18 | +		JLabel label=new JLabel(name);  | 
|---|
 | 19 | +		add(label);  | 
|---|
 | 20 | +		label.setBounds(3,3,50,30);  | 
|---|
 | 21 | +		add(line);  | 
|---|
 | 22 | +	}  | 
|---|
 | 23 | +	  | 
|---|
 | 24 | +	public void addContent(JComponent c)  | 
|---|
 | 25 | +	{  | 
|---|
 | 26 | +		add(c);  | 
|---|
 | 27 | +		c.setBorder(null);  | 
|---|
 | 28 | +		c.setBounds(x1,10+numComps*compH, c.getPreferredSize().width, c.getPreferredSize().height);  | 
|---|
 | 29 | +		numComps++;  | 
|---|
 | 30 | +		line.setBounds(x1-10,10,1,numComps*compH-5);  | 
|---|
 | 31 | +	}  | 
|---|
 | 32 | +	  | 
|---|
 | 33 | +	public Dimension getPreferredSize()  | 
|---|
 | 34 | +	{  | 
|---|
 | 35 | +		return new Dimension(width, 20+compH*numComps);  | 
|---|
 | 36 | +	}  | 
|---|
 | 37 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.ui;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.model.*;  | 
|---|
 | 4 | +import timeflow.data.time.*;  | 
|---|
 | 5 | +import timeflow.data.db.*;  | 
|---|
 | 6 | +import timeflow.data.db.filter.*;  | 
|---|
 | 7 | +import timeflow.format.field.*;  | 
|---|
 | 8 | +import timeflow.format.file.TimeflowFormat;  | 
|---|
 | 9 | +  | 
|---|
 | 10 | +import javax.swing.*;  | 
|---|
 | 11 | +import java.util.*;  | 
|---|
 | 12 | +import java.awt.*;  | 
|---|
 | 13 | +import java.awt.event.*;  | 
|---|
 | 14 | +  | 
|---|
 | 15 | +  | 
|---|
 | 16 | +public class DateFieldPanel extends JPanel  | 
|---|
 | 17 | +{  | 
|---|
 | 18 | +	TFModel model;  | 
|---|
 | 19 | +	int numRows;  | 
|---|
 | 20 | +	HashMap<String, Integer> numBad=new HashMap<String, Integer>();  | 
|---|
 | 21 | +	private static String[] mappable={VirtualField.START, VirtualField.END};  | 
|---|
 | 22 | +	JLabel status=new JLabel("");  | 
|---|
 | 23 | +	FieldMap[] panels=new FieldMap[mappable.length];  | 
|---|
 | 24 | +	JButton submit, cancel;  | 
|---|
 | 25 | +	  | 
|---|
 | 26 | +	public DateFieldPanel(TFModel model, boolean hasButtons)  | 
|---|
 | 27 | +	{  | 
|---|
 | 28 | +		this.model=model;  | 
|---|
 | 29 | +		  | 
|---|
 | 30 | +		ActDB db=model.getDB();  | 
|---|
 | 31 | +		numRows=db.size();  | 
|---|
 | 32 | +		ActList all=db.all();  | 
|---|
 | 33 | +		  | 
|---|
 | 34 | +		  | 
|---|
 | 35 | +		// calculate stats.  | 
|---|
 | 36 | +		for (Field f: db.getFields(RoughTime.class))  | 
|---|
 | 37 | +		{  | 
|---|
 | 38 | +			int bad=DBUtils.count(all, new MissingValueFilter(f));  | 
|---|
 | 39 | +			numBad.put(f.getName(),bad);  | 
|---|
 | 40 | +		}  | 
|---|
 | 41 | +		  | 
|---|
 | 42 | +		setLayout(new BorderLayout());  | 
|---|
 | 43 | +		JPanel top=new JPanel();  | 
|---|
 | 44 | +		if (hasButtons)  | 
|---|
 | 45 | +		{  | 
|---|
 | 46 | +			submit=new JButton("Submit");  | 
|---|
 | 47 | +			top.add(submit);  | 
|---|
 | 48 | +			cancel=new JButton("Cancel");  | 
|---|
 | 49 | +			top.add(cancel);  | 
|---|
 | 50 | +		}  | 
|---|
 | 51 | +		else  | 
|---|
 | 52 | +		{  | 
|---|
 | 53 | +			JLabel about=new JLabel("Dates");  | 
|---|
 | 54 | +			top.add(about);  | 
|---|
 | 55 | +		}  | 
|---|
 | 56 | +		top.add(status);  | 
|---|
 | 57 | +		status.setForeground(Color.red);  | 
|---|
 | 58 | +		add(top, BorderLayout.SOUTH);  | 
|---|
 | 59 | +		JPanel bottom=new JPanel();  | 
|---|
 | 60 | +		add(bottom, BorderLayout.CENTER);  | 
|---|
 | 61 | +		bottom.setLayout(new GridLayout(mappable.length,1));  | 
|---|
 | 62 | +		  | 
|---|
 | 63 | +		// add panels.  | 
|---|
 | 64 | +		for (int i=0; i<mappable.length; i++)  | 
|---|
 | 65 | +		{  | 
|---|
 | 66 | +			panels[i]=new FieldMap(mappable[i],i==0);  | 
|---|
 | 67 | +			bottom.add(panels[i]);  | 
|---|
 | 68 | +		}  | 
|---|
 | 69 | +		  | 
|---|
 | 70 | +		  | 
|---|
 | 71 | +		// to do: add a status field or something  | 
|---|
 | 72 | +		// note inconsistencies, like:  | 
|---|
 | 73 | +		// * no start defined.  | 
|---|
 | 74 | +		// * ends after starts  | 
|---|
 | 75 | +	}  | 
|---|
 | 76 | +	  | 
|---|
 | 77 | +	public void map()  | 
|---|
 | 78 | +	{  | 
|---|
 | 79 | +		ActDB db=model.getDB();  | 
|---|
 | 80 | +		for (int i=0; i<panels.length; i++)  | 
|---|
 | 81 | +		{  | 
|---|
 | 82 | +			String choice=(String)panels[i].choices.getSelectedItem();  | 
|---|
 | 83 | +			db.setAlias("None".equals(choice) ? null : db.getField(choice), panels[i].name);  | 
|---|
 | 84 | +		}   | 
|---|
 | 85 | +		model.noteSchemaChange(this);  | 
|---|
 | 86 | +	}  | 
|---|
 | 87 | +	  | 
|---|
 | 88 | +	class FieldMap extends JPanel  | 
|---|
 | 89 | +	{  | 
|---|
 | 90 | +		String name;  | 
|---|
 | 91 | +		int bad;  | 
|---|
 | 92 | +		JComboBox choices;  | 
|---|
 | 93 | +		JLabel definedLabel=new JLabel("# def goes here");  | 
|---|
 | 94 | +		boolean important;  | 
|---|
 | 95 | +		  | 
|---|
 | 96 | +		FieldMap(String name, boolean important)  | 
|---|
 | 97 | +		{  | 
|---|
 | 98 | +			this.name=name;  | 
|---|
 | 99 | +			this.important=important;  | 
|---|
 | 100 | +			setBackground(Color.white);  | 
|---|
 | 101 | +			setLayout(new GridLayout(1,3));  | 
|---|
 | 102 | +			  | 
|---|
 | 103 | +			add(new JLabel("   "+(important? "* ":"")+VirtualField.humanName(name)));//, BorderLayout.NORTH);  | 
|---|
 | 104 | +			  | 
|---|
 | 105 | +			choices=new JComboBox();  | 
|---|
 | 106 | +			choices.addItem("None");  | 
|---|
 | 107 | +			for (Field f: model.getDB().getFields(RoughTime.class))  | 
|---|
 | 108 | +			{				  | 
|---|
 | 109 | +				choices.addItem(f.getName());//+"  "+percentDef+"% defined");  | 
|---|
 | 110 | +			}  | 
|---|
 | 111 | +			add(choices);  | 
|---|
 | 112 | +			choices.addActionListener(new ActionListener() {  | 
|---|
 | 113 | +				@Override  | 
|---|
 | 114 | +				public void actionPerformed(ActionEvent e) {  | 
|---|
 | 115 | +					showDefined();  | 
|---|
 | 116 | +				}});  | 
|---|
 | 117 | +			  | 
|---|
 | 118 | +			add(definedLabel);  | 
|---|
 | 119 | +			definedLabel.setForeground(Color.gray);  | 
|---|
 | 120 | +			  | 
|---|
 | 121 | +			Field current=model.getDB().getField(name);  | 
|---|
 | 122 | +			if (current!=null)  | 
|---|
 | 123 | +				choices.setSelectedItem(current.getName());  | 
|---|
 | 124 | +			  | 
|---|
 | 125 | +			showDefined();  | 
|---|
 | 126 | +		}  | 
|---|
 | 127 | +		  | 
|---|
 | 128 | +		void showDefined()  | 
|---|
 | 129 | +		{  | 
|---|
 | 130 | +			String choice=(String)choices.getSelectedItem();  | 
|---|
 | 131 | +			String val="";  | 
|---|
 | 132 | +			boolean none="None".equals(choice);  | 
|---|
 | 133 | +			int percentDef=0;  | 
|---|
 | 134 | +			if (!none)  | 
|---|
 | 135 | +			{  | 
|---|
 | 136 | +				percentDef=(int)(100*(1-numBad.get(choice)/(double)numRows));  | 
|---|
 | 137 | +				val="  "+percentDef+"% defined";  | 
|---|
 | 138 | +			}  | 
|---|
 | 139 | +			definedLabel.setText(val);  | 
|---|
 | 140 | +			if (important)  | 
|---|
 | 141 | +			{  | 
|---|
 | 142 | +				if (none)  | 
|---|
 | 143 | +					status.setText("   Need \"Start\" for timeline/calendar.");  | 
|---|
 | 144 | +				else if (percentDef==0)  | 
|---|
 | 145 | +					status.setText("   No dates defined in "+choice+".");  | 
|---|
 | 146 | +				else  | 
|---|
 | 147 | +					status.setText("");  | 
|---|
 | 148 | +			}  | 
|---|
 | 149 | +		}  | 
|---|
 | 150 | +	}  | 
|---|
 | 151 | +	  | 
|---|
 | 152 | +	public Dimension getPreferredSize()  | 
|---|
 | 153 | +	{  | 
|---|
 | 154 | +		return new Dimension(400,80+mappable.length*25);  | 
|---|
 | 155 | +	}  | 
|---|
 | 156 | +	  | 
|---|
 | 157 | +	public static void popWindow(TFModel model)  | 
|---|
 | 158 | +	{  | 
|---|
 | 159 | +		final JFrame window=new JFrame("Date Fields");  | 
|---|
 | 160 | +		window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  | 
|---|
 | 161 | +		window.getContentPane().setLayout(new GridLayout(1,1));  | 
|---|
 | 162 | +		final DateFieldPanel p=new DateFieldPanel(model, true);  | 
|---|
 | 163 | +		p.submit.addActionListener(new ActionListener() {  | 
|---|
 | 164 | +			@Override  | 
|---|
 | 165 | +			public void actionPerformed(ActionEvent e) {  | 
|---|
 | 166 | +				p.map();  | 
|---|
 | 167 | +				window.setVisible(false);  | 
|---|
 | 168 | +			}});  | 
|---|
 | 169 | +		p.cancel.addActionListener(new ActionListener() {  | 
|---|
 | 170 | +			@Override  | 
|---|
 | 171 | +			public void actionPerformed(ActionEvent e) {  | 
|---|
 | 172 | +				window.setVisible(false);  | 
|---|
 | 173 | +			}});  | 
|---|
 | 174 | +		window.getContentPane().add(p);  | 
|---|
 | 175 | +		window.setBounds(50,50,window.getPreferredSize().width,window.getPreferredSize().height);  | 
|---|
 | 176 | +		window.setVisible(true);  | 
|---|
 | 177 | +	}  | 
|---|
 | 178 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.ui;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import javax.swing.*;  | 
|---|
 | 4 | +import java.awt.*;  | 
|---|
 | 5 | +  | 
|---|
 | 6 | +public class DottedLine extends JPanel  | 
|---|
 | 7 | +{  | 
|---|
 | 8 | +	public void paintComponent(Graphics g)  | 
|---|
 | 9 | +	{  | 
|---|
 | 10 | +		int w=getSize().width, h=getSize().height;  | 
|---|
 | 11 | +		g.setColor(Color.white);  | 
|---|
 | 12 | +		g.fillRect(0,0,w,h);  | 
|---|
 | 13 | +		g.setColor(Color.lightGray);  | 
|---|
 | 14 | +		if (w>h)  | 
|---|
 | 15 | +		{  | 
|---|
 | 16 | +			for (int x=0; x<w; x+=4)  | 
|---|
 | 17 | +			{  | 
|---|
 | 18 | +				g.drawLine(x,0,x+1,0);  | 
|---|
 | 19 | +			}  | 
|---|
 | 20 | +		}  | 
|---|
 | 21 | +		else  | 
|---|
 | 22 | +		{  | 
|---|
 | 23 | +			for (int y=0; y<h; y+=4)  | 
|---|
 | 24 | +			{  | 
|---|
 | 25 | +				g.drawLine(0,y,0,y+1);  | 
|---|
 | 26 | +			}			  | 
|---|
 | 27 | +		}  | 
|---|
 | 28 | +	}  | 
|---|
 | 29 | +	  | 
|---|
 | 30 | +	public Dimension getPreferredSize()  | 
|---|
 | 31 | +	{  | 
|---|
 | 32 | +		return new Dimension(1,1);  | 
|---|
 | 33 | +	}  | 
|---|
 | 34 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.ui;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.model.*;  | 
|---|
 | 4 | +import timeflow.app.ui.ImportDelimitedPanel.SchemaPanel;  | 
|---|
 | 5 | +import timeflow.data.time.*;  | 
|---|
 | 6 | +import timeflow.data.db.*;  | 
|---|
 | 7 | +  | 
|---|
 | 8 | +import javax.swing.*;  | 
|---|
 | 9 | +  | 
|---|
 | 10 | +import java.util.*;  | 
|---|
 | 11 | +import java.awt.*;  | 
|---|
 | 12 | +import java.awt.event.ActionEvent;  | 
|---|
 | 13 | +import java.awt.event.ActionListener;  | 
|---|
 | 14 | +  | 
|---|
 | 15 | +// panel with form for editing a given database entry  | 
|---|
 | 16 | +public class EditRecordPanel extends JPanel  | 
|---|
 | 17 | +{  | 
|---|
 | 18 | +  | 
|---|
 | 19 | +        Act act;  | 
|---|
 | 20 | +        HashMap<Field, EditValuePanel> fieldUI = new HashMap<Field, EditValuePanel>();  | 
|---|
 | 21 | +        JButton submit, cancel;  | 
|---|
 | 22 | +        Dimension idealSize = new Dimension();  | 
|---|
 | 23 | +        TFModel model;  | 
|---|
 | 24 | +  | 
|---|
 | 25 | +        private static void edit(final TFModel model, final Act act, final boolean isAdd)  | 
|---|
 | 26 | +        {  | 
|---|
 | 27 | +                final JFrame window = new JFrame(isAdd ? "Add Record" : "Edit Record");  | 
|---|
 | 28 | +                window.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);  | 
|---|
 | 29 | +                final EditRecordPanel editor = new EditRecordPanel(model, act);  | 
|---|
 | 30 | +                window.getContentPane().setLayout(new GridLayout(1, 1));  | 
|---|
 | 31 | +                window.getContentPane().add(editor);  | 
|---|
 | 32 | +                editor.submit.addActionListener(new ActionListener()  | 
|---|
 | 33 | +                {  | 
|---|
 | 34 | +  | 
|---|
 | 35 | +                        @Override  | 
|---|
 | 36 | +                        public void actionPerformed(ActionEvent e)  | 
|---|
 | 37 | +                        {  | 
|---|
 | 38 | +                                window.setVisible(false);  | 
|---|
 | 39 | +                                editor.submitValues();  | 
|---|
 | 40 | +                                model.noteAdd(this);  | 
|---|
 | 41 | +                        }  | 
|---|
 | 42 | +                });  | 
|---|
 | 43 | +                editor.cancel.addActionListener(new ActionListener()  | 
|---|
 | 44 | +                {  | 
|---|
 | 45 | +  | 
|---|
 | 46 | +                        @Override  | 
|---|
 | 47 | +                        public void actionPerformed(ActionEvent e)  | 
|---|
 | 48 | +                        {  | 
|---|
 | 49 | +                                window.setVisible(false);  | 
|---|
 | 50 | +                                if (isAdd)  | 
|---|
 | 51 | +                                {  | 
|---|
 | 52 | +                                        model.getDB().delete(act);  | 
|---|
 | 53 | +                                }  | 
|---|
 | 54 | +                        }  | 
|---|
 | 55 | +                });  | 
|---|
 | 56 | +                window.setBounds(50, 50, 700, 500);  | 
|---|
 | 57 | +                window.pack();  | 
|---|
 | 58 | +                window.setVisible(true);  | 
|---|
 | 59 | +        }  | 
|---|
 | 60 | +  | 
|---|
 | 61 | +        public static void edit(TFModel model, Act act)  | 
|---|
 | 62 | +        {  | 
|---|
 | 63 | +                edit(model, act, false);  | 
|---|
 | 64 | +        }  | 
|---|
 | 65 | +  | 
|---|
 | 66 | +        public static void add(TFModel model)  | 
|---|
 | 67 | +        {  | 
|---|
 | 68 | +                Act act = model.getDB().createAct();  | 
|---|
 | 69 | +                edit(model, act, true);  | 
|---|
 | 70 | +        }  | 
|---|
 | 71 | +  | 
|---|
 | 72 | +        public static void add(TFModel model, RoughTime r)  | 
|---|
 | 73 | +        {  | 
|---|
 | 74 | +                Act act = model.getDB().createAct();  | 
|---|
 | 75 | +                act.set(act.getDB().getField(VirtualField.START), r);  | 
|---|
 | 76 | +                edit(model, act, true);  | 
|---|
 | 77 | +        }  | 
|---|
 | 78 | +  | 
|---|
 | 79 | +        public EditRecordPanel(TFModel model, Act act)  | 
|---|
 | 80 | +        {  | 
|---|
 | 81 | +                this.model = model;  | 
|---|
 | 82 | +                this.act = act;  | 
|---|
 | 83 | +  | 
|---|
 | 84 | +                setBackground(Color.white);  | 
|---|
 | 85 | +                setLayout(new BorderLayout());  | 
|---|
 | 86 | +  | 
|---|
 | 87 | +                JPanel buttons = new JPanel();  | 
|---|
 | 88 | +                add(buttons, BorderLayout.SOUTH);  | 
|---|
 | 89 | +                buttons.setBackground(Color.lightGray);  | 
|---|
 | 90 | +                submit = new JButton("OK");  | 
|---|
 | 91 | +                buttons.add(submit);  | 
|---|
 | 92 | +                cancel = new JButton("Cancel");  | 
|---|
 | 93 | +                buttons.add(cancel);  | 
|---|
 | 94 | +  | 
|---|
 | 95 | +                JPanel entryPanel = new JPanel();  | 
|---|
 | 96 | +                JScrollPane scroller = new JScrollPane(entryPanel);  | 
|---|
 | 97 | +                add(scroller, BorderLayout.CENTER);  | 
|---|
 | 98 | +  | 
|---|
 | 99 | +                java.util.List<Field> fields = act.getDB().getFields();  | 
|---|
 | 100 | +                int n = fields.size();  | 
|---|
 | 101 | +                entryPanel.setLayout(null);  | 
|---|
 | 102 | +  | 
|---|
 | 103 | +                DBUtils.setRecSizesFromCurrent(act.getDB());  | 
|---|
 | 104 | +                int top = 0;  | 
|---|
 | 105 | +  | 
|---|
 | 106 | +                for (Field f : fields)  | 
|---|
 | 107 | +                {  | 
|---|
 | 108 | +                        EditValuePanel p = new EditValuePanel(f.getName(), act.get(f),  | 
|---|
 | 109 | +                                f.getType(), f.getRecommendedSize() > 100);  | 
|---|
 | 110 | +                        entryPanel.add(p);  | 
|---|
 | 111 | +                        Dimension d = p.getPreferredSize();  | 
|---|
 | 112 | +                        p.setBounds(0, top, d.width, d.height);  | 
|---|
 | 113 | +                        top += d.height;  | 
|---|
 | 114 | +                        idealSize.width = Math.max(d.width + 5, idealSize.width);  | 
|---|
 | 115 | +                        idealSize.height = Math.max(top + 45, idealSize.height);  | 
|---|
 | 116 | +                        fieldUI.put(f, p);  | 
|---|
 | 117 | +                }  | 
|---|
 | 118 | +  | 
|---|
 | 119 | +        }  | 
|---|
 | 120 | +  | 
|---|
 | 121 | +        public Dimension getPreferredSize()  | 
|---|
 | 122 | +        {  | 
|---|
 | 123 | +                return idealSize;  | 
|---|
 | 124 | +        }  | 
|---|
 | 125 | +  | 
|---|
 | 126 | +        public void submitValues()  | 
|---|
 | 127 | +        {  | 
|---|
 | 128 | +                for (Field f : fieldUI.keySet())  | 
|---|
 | 129 | +                {  | 
|---|
 | 130 | +                        act.set(f, fieldUI.get(f).getInputValue());  | 
|---|
 | 131 | +                }  | 
|---|
 | 132 | +                model.noteRecordChange(this);  | 
|---|
 | 133 | +        }  | 
|---|
 | 134 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.ui;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.format.field.*;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +import javax.swing.*;  | 
|---|
 | 6 | +import javax.swing.text.JTextComponent;  | 
|---|
 | 7 | +  | 
|---|
 | 8 | +import java.awt.*;  | 
|---|
 | 9 | +import java.awt.event.*;  | 
|---|
 | 10 | +  | 
|---|
 | 11 | +public class EditValuePanel extends JPanel  | 
|---|
 | 12 | +{  | 
|---|
 | 13 | +  | 
|---|
 | 14 | +        FieldFormat parser;  | 
|---|
 | 15 | +        boolean longField;  | 
|---|
 | 16 | +        JLabel feedback = new JLabel()  | 
|---|
 | 17 | +        {  | 
|---|
 | 18 | +  | 
|---|
 | 19 | +                public Dimension getPreferredSize()  | 
|---|
 | 20 | +                {  | 
|---|
 | 21 | +                        Dimension d = super.getPreferredSize();  | 
|---|
 | 22 | +                        return new Dimension(200, d.height);  | 
|---|
 | 23 | +                }  | 
|---|
 | 24 | +        };  | 
|---|
 | 25 | +        static final String space = "   ";  | 
|---|
 | 26 | +        JTextComponent input;  | 
|---|
 | 27 | +  | 
|---|
 | 28 | +        public EditValuePanel(String name, Object startValue, Class type, boolean longField)  | 
|---|
 | 29 | +        {  | 
|---|
 | 30 | +                parser = FieldFormatCatalog.getFormat(type);  | 
|---|
 | 31 | +  | 
|---|
 | 32 | +                if (longField)  | 
|---|
 | 33 | +                {  | 
|---|
 | 34 | +                        setLayout(new BorderLayout());  | 
|---|
 | 35 | +                        JPanel top = new JPanel();  | 
|---|
 | 36 | +                        top.setLayout(new GridLayout(2, 2));  | 
|---|
 | 37 | +                        top.add(new JPanel());  | 
|---|
 | 38 | +                        top.add(new JPanel());  | 
|---|
 | 39 | +                        JLabel fieldLabel = new JLabel(space + name + "   (long)");  | 
|---|
 | 40 | +                        top.add(fieldLabel);  | 
|---|
 | 41 | +                        top.add(feedback);  | 
|---|
 | 42 | +                        add(top, BorderLayout.NORTH);  | 
|---|
 | 43 | +                        input = new JTextArea(5, 60);  | 
|---|
 | 44 | +                        ((JTextArea) input).setLineWrap(true);  | 
|---|
 | 45 | +                        ((JTextArea) input).setWrapStyleWord(true);  | 
|---|
 | 46 | +                        JScrollPane scroller = new JScrollPane(input);  | 
|---|
 | 47 | +                        scroller.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);  | 
|---|
 | 48 | +                        add(scroller, BorderLayout.CENTER);  | 
|---|
 | 49 | +                        add(new JPanel(), BorderLayout.WEST);  | 
|---|
 | 50 | +                        add(new JPanel(), BorderLayout.SOUTH);  | 
|---|
 | 51 | +                } else  | 
|---|
 | 52 | +                {  | 
|---|
 | 53 | +                        setLayout(new GridLayout(1, 4));  | 
|---|
 | 54 | +                        JLabel fieldLabel = new JLabel(space + name);  | 
|---|
 | 55 | +                        add(fieldLabel);  | 
|---|
 | 56 | +                        JLabel typeLabel = new JLabel(FieldFormatCatalog.humanName(type));  | 
|---|
 | 57 | +                        add(typeLabel);  | 
|---|
 | 58 | +                        typeLabel.setForeground(Color.gray);  | 
|---|
 | 59 | +                        input = new JTextField(8);  | 
|---|
 | 60 | +                        add(input);  | 
|---|
 | 61 | +                        // enough room for "couldn't understand"  | 
|---|
 | 62 | +                        add(feedback);  | 
|---|
 | 63 | +                }  | 
|---|
 | 64 | +                input.setText(startValue == null ? "" : parser.format(startValue));  | 
|---|
 | 65 | +                input.addKeyListener(new KeyAdapter()  | 
|---|
 | 66 | +                {  | 
|---|
 | 67 | +  | 
|---|
 | 68 | +                        @Override  | 
|---|
 | 69 | +                        public void keyReleased(KeyEvent e)  | 
|---|
 | 70 | +                        {  | 
|---|
 | 71 | +                                parse();  | 
|---|
 | 72 | +                        }  | 
|---|
 | 73 | +                });  | 
|---|
 | 74 | +                parse();  | 
|---|
 | 75 | +        }  | 
|---|
 | 76 | +  | 
|---|
 | 77 | +        void parse()  | 
|---|
 | 78 | +        {  | 
|---|
 | 79 | +                try  | 
|---|
 | 80 | +                {  | 
|---|
 | 81 | +                        parser.parse(input.getText());  | 
|---|
 | 82 | +                } catch (Exception e)  | 
|---|
 | 83 | +                {  | 
|---|
 | 84 | +                }  | 
|---|
 | 85 | +                feedback.setText("  " + parser.feedback());  | 
|---|
 | 86 | +                feedback.setForeground(parser.isUnderstood() ? Color.gray : Color.red);  | 
|---|
 | 87 | +        }  | 
|---|
 | 88 | +  | 
|---|
 | 89 | +        public Object getInputValue()  | 
|---|
 | 90 | +        {  | 
|---|
 | 91 | +                try  | 
|---|
 | 92 | +                {  | 
|---|
 | 93 | +                        return parser.parse(input.getText());  | 
|---|
 | 94 | +                } catch (Exception e)  | 
|---|
 | 95 | +                {  | 
|---|
 | 96 | +                        return null;  | 
|---|
 | 97 | +                }  | 
|---|
 | 98 | +        }  | 
|---|
 | 99 | +  | 
|---|
 | 100 | +        public boolean isOK()  | 
|---|
 | 101 | +        {  | 
|---|
 | 102 | +                return parser.isUnderstood();  | 
|---|
 | 103 | +        }  | 
|---|
 | 104 | +  | 
|---|
 | 105 | +        public Dimension getPreferredSize()  | 
|---|
 | 106 | +        {  | 
|---|
 | 107 | +                Dimension d = super.getPreferredSize();  | 
|---|
 | 108 | +                int w = Math.max(300, d.width);  | 
|---|
 | 109 | +                return new Dimension(w, d.height);  | 
|---|
 | 110 | +        }  | 
|---|
 | 111 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.ui;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.app.ui.filter.FilterControlPanel;  | 
|---|
 | 4 | +import timeflow.data.db.*;  | 
|---|
 | 5 | +import timeflow.data.time.*;  | 
|---|
 | 6 | +import timeflow.model.*;  | 
|---|
 | 7 | +  | 
|---|
 | 8 | +import javax.swing.*;  | 
|---|
 | 9 | +  | 
|---|
 | 10 | +import timeflow.util.*;  | 
|---|
 | 11 | +  | 
|---|
 | 12 | +import java.awt.*;  | 
|---|
 | 13 | +import java.awt.event.*;  | 
|---|
 | 14 | +import java.util.List;  | 
|---|
 | 15 | +  | 
|---|
 | 16 | +public class GlobalDisplayPanel extends ModelPanel  | 
|---|
 | 17 | +{  | 
|---|
 | 18 | +  | 
|---|
 | 19 | +        JPanel encodings = new JPanel();  | 
|---|
 | 20 | +        JPanel localControls = new JPanel();  | 
|---|
 | 21 | +        JPanel globalControls = new JPanel();  | 
|---|
 | 22 | +        CardLayout localCards = new CardLayout();  | 
|---|
 | 23 | +  | 
|---|
 | 24 | +        public GlobalDisplayPanel(TFModel model, FilterControlPanel filterControls)  | 
|---|
 | 25 | +        {  | 
|---|
 | 26 | +                super(model);  | 
|---|
 | 27 | +                setBackground(Color.white);  | 
|---|
 | 28 | +                setLayout(new BorderLayout());  | 
|---|
 | 29 | +  | 
|---|
 | 30 | +                add(localControls, BorderLayout.CENTER);  | 
|---|
 | 31 | +                localControls.setBackground(Color.white);  | 
|---|
 | 32 | +                localControls.setLayout(localCards);  | 
|---|
 | 33 | +  | 
|---|
 | 34 | +                JPanel p = new JPanel();  | 
|---|
 | 35 | +                p.setBackground(Color.white);  | 
|---|
 | 36 | +                p.setLayout(new BorderLayout());  | 
|---|
 | 37 | +  | 
|---|
 | 38 | +                JPanel globalLabel = new JPanel();  | 
|---|
 | 39 | +                globalLabel.setLayout(new BorderLayout());  | 
|---|
 | 40 | +  | 
|---|
 | 41 | +                JPanel topLine = new Pad(2, 3);  | 
|---|
 | 42 | +                topLine.setBackground(Color.gray);  | 
|---|
 | 43 | +                globalLabel.add(topLine, BorderLayout.NORTH);  | 
|---|
 | 44 | +  | 
|---|
 | 45 | +                JPanel bottomLine = new Pad(2, 3);  | 
|---|
 | 46 | +                bottomLine.setBackground(Color.gray);  | 
|---|
 | 47 | +                globalLabel.add(bottomLine, BorderLayout.SOUTH);  | 
|---|
 | 48 | +  | 
|---|
 | 49 | +                JLabel label = new JLabel(" Global Controls", JLabel.LEFT)  | 
|---|
 | 50 | +                {  | 
|---|
 | 51 | +  | 
|---|
 | 52 | +                        public Dimension getPreferredSize()  | 
|---|
 | 53 | +                        {  | 
|---|
 | 54 | +                                return new Dimension(30, 30);  | 
|---|
 | 55 | +                        }  | 
|---|
 | 56 | +                };  | 
|---|
 | 57 | +                label.setBackground(Color.lightGray);  | 
|---|
 | 58 | +                label.setForeground(Color.darkGray);  | 
|---|
 | 59 | +                globalLabel.add(label, BorderLayout.CENTER);  | 
|---|
 | 60 | +                p.add(globalLabel, BorderLayout.NORTH);  | 
|---|
 | 61 | +  | 
|---|
 | 62 | +                JPanel global = new JPanel();  | 
|---|
 | 63 | +                global.setLayout(new BorderLayout());  | 
|---|
 | 64 | +                global.add(new StatusPanel(model, filterControls), BorderLayout.NORTH);  | 
|---|
 | 65 | +  | 
|---|
 | 66 | +                encodings.setLayout(new GridLayout(4, 1));  | 
|---|
 | 67 | +                encodings.setBackground(Color.white);  | 
|---|
 | 68 | +                global.add(encodings, BorderLayout.CENTER);  | 
|---|
 | 69 | +                p.add(global, BorderLayout.CENTER);  | 
|---|
 | 70 | +                add(p, BorderLayout.SOUTH);  | 
|---|
 | 71 | +  | 
|---|
 | 72 | +                makeEncodingPanel();  | 
|---|
 | 73 | +        }  | 
|---|
 | 74 | +  | 
|---|
 | 75 | +        public void showLocalControl(String name)  | 
|---|
 | 76 | +        {  | 
|---|
 | 77 | +                localCards.show(localControls, name);  | 
|---|
 | 78 | +        }  | 
|---|
 | 79 | +  | 
|---|
 | 80 | +        public void addLocalControl(String name, JComponent control)  | 
|---|
 | 81 | +        {  | 
|---|
 | 82 | +                localControls.add(control, name);  | 
|---|
 | 83 | +        }  | 
|---|
 | 84 | +  | 
|---|
 | 85 | +        void makeEncodingPanel()  | 
|---|
 | 86 | +        {  | 
|---|
 | 87 | +                encodings.removeAll();  | 
|---|
 | 88 | +                ActDB db = getModel().getDB();  | 
|---|
 | 89 | +                if (db == null)  | 
|---|
 | 90 | +                {  | 
|---|
 | 91 | +                        return;  | 
|---|
 | 92 | +                }  | 
|---|
 | 93 | +  | 
|---|
 | 94 | +                java.util.List<Field> dimensions = DBUtils.categoryFields(db);  | 
|---|
 | 95 | +                java.util.List<Field> measures = db.getFields(Double.class);  | 
|---|
 | 96 | +                java.util.List<Field> fields = db.getFields();  | 
|---|
 | 97 | +  | 
|---|
 | 98 | +                makeChooser(VirtualField.LABEL, "Label", "None", fields); //db.getFields()); // String.class));  | 
|---|
 | 99 | +                makeChooser(VirtualField.TRACK, "Groups", "None", dimensions);  | 
|---|
 | 100 | +                makeChooser(VirtualField.COLOR, "Color", "Same As Groups", dimensions);  | 
|---|
 | 101 | +  | 
|---|
 | 102 | +                makeChooser(VirtualField.SIZE, "Dot Size", "None", measures);  | 
|---|
 | 103 | +        }  | 
|---|
 | 104 | +  | 
|---|
 | 105 | +        private JComboBox makeChooser(final String alias, String title, String nothingLabel, List<Field> fields)  | 
|---|
 | 106 | +        {  | 
|---|
 | 107 | +                if (fields.size() == 0)  | 
|---|
 | 108 | +                {  | 
|---|
 | 109 | +                        return null;  | 
|---|
 | 110 | +                }  | 
|---|
 | 111 | +                JPanel panel = new JPanel();  | 
|---|
 | 112 | +                panel.setBackground(Color.white);  | 
|---|
 | 113 | +                panel.setLayout(new BorderLayout());  | 
|---|
 | 114 | +                JPanel topPad = new Pad(10, 7);  | 
|---|
 | 115 | +                topPad.setBackground(Color.white);  | 
|---|
 | 116 | +                panel.add(topPad, BorderLayout.NORTH);  | 
|---|
 | 117 | +  | 
|---|
 | 118 | +                JPanel rightPad = new Pad(10, 10);  | 
|---|
 | 119 | +                panel.add(rightPad, BorderLayout.EAST);  | 
|---|
 | 120 | +                rightPad.setBackground(Color.white);  | 
|---|
 | 121 | +  | 
|---|
 | 122 | +                panel.add(new JLabel(" " + title)  | 
|---|
 | 123 | +                {  | 
|---|
 | 124 | +  | 
|---|
 | 125 | +                        public Dimension getPreferredSize()  | 
|---|
 | 126 | +                        {  | 
|---|
 | 127 | +                                return new Dimension(60, 25);  | 
|---|
 | 128 | +                        }  | 
|---|
 | 129 | +                },  | 
|---|
 | 130 | +                        BorderLayout.WEST);  | 
|---|
 | 131 | +                final JComboBox c = new JComboBox();  | 
|---|
 | 132 | +  | 
|---|
 | 133 | +                if (nothingLabel != null)  | 
|---|
 | 134 | +                {  | 
|---|
 | 135 | +                        c.addItem(nothingLabel);  | 
|---|
 | 136 | +                }  | 
|---|
 | 137 | +                for (Field f : fields)  | 
|---|
 | 138 | +                {  | 
|---|
 | 139 | +                        c.addItem(f.getName());  | 
|---|
 | 140 | +                }  | 
|---|
 | 141 | +  | 
|---|
 | 142 | +                Field current = getModel().getDB().getField(alias);  | 
|---|
 | 143 | +                if (current != null)  | 
|---|
 | 144 | +                {  | 
|---|
 | 145 | +                        c.setSelectedItem(current.getName());  | 
|---|
 | 146 | +                }  | 
|---|
 | 147 | +                c.addActionListener(new ActionListener()  | 
|---|
 | 148 | +                {  | 
|---|
 | 149 | +  | 
|---|
 | 150 | +                        @Override  | 
|---|
 | 151 | +                        public void actionPerformed(ActionEvent e)  | 
|---|
 | 152 | +                        {  | 
|---|
 | 153 | +                                Field realField = c.getSelectedIndex() == 0  | 
|---|
 | 154 | +                                        ? null : getModel().getDB().getField((String) c.getSelectedItem());  | 
|---|
 | 155 | +                                getModel().setFieldAlias(realField, alias, GlobalDisplayPanel.this);  | 
|---|
 | 156 | +                        }  | 
|---|
 | 157 | +                });  | 
|---|
 | 158 | +                c.setBackground(Color.white);  | 
|---|
 | 159 | +                c.setBorder(null);  | 
|---|
 | 160 | +                panel.add(c, BorderLayout.CENTER);  | 
|---|
 | 161 | +                encodings.add(panel);  | 
|---|
 | 162 | +                c.setBorder(null);  | 
|---|
 | 163 | +                return c;  | 
|---|
 | 164 | +        }  | 
|---|
 | 165 | +  | 
|---|
 | 166 | +        @Override  | 
|---|
 | 167 | +        public void note(TFEvent e)  | 
|---|
 | 168 | +        {  | 
|---|
 | 169 | +                if (e.affectsSchema())  | 
|---|
 | 170 | +                {  | 
|---|
 | 171 | +                        makeEncodingPanel();  | 
|---|
 | 172 | +                }  | 
|---|
 | 173 | +        }  | 
|---|
 | 174 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.ui;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import java.awt.Font;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +import javax.swing.JEditorPane;  | 
|---|
 | 6 | +import javax.swing.UIManager;  | 
|---|
 | 7 | +import javax.swing.text.html.HTMLDocument;  | 
|---|
 | 8 | +import javax.swing.text.html.StyleSheet;  | 
|---|
 | 9 | +  | 
|---|
 | 10 | +public class HtmlDisplay {  | 
|---|
 | 11 | +	public static JEditorPane create()  | 
|---|
 | 12 | +	{  | 
|---|
 | 13 | +		JEditorPane p = new JEditorPane();  | 
|---|
 | 14 | +		p.setEditable(false);  | 
|---|
 | 15 | +		p.setContentType("text/html");  | 
|---|
 | 16 | +		  | 
|---|
 | 17 | +		Font font = UIManager.getFont("Label.font");  | 
|---|
 | 18 | +        String bodyRule = "body { font-family: "+font.getFamily()+"; "+  | 
|---|
 | 19 | +                "font-size: " + font.getSize() + "pt; }";  | 
|---|
 | 20 | +        StyleSheet styles=((HTMLDocument)p.getDocument()).getStyleSheet();  | 
|---|
 | 21 | +        styles.addRule(bodyRule);  | 
|---|
 | 22 | +        return p;  | 
|---|
 | 23 | +	}  | 
|---|
 | 24 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.ui;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.data.time.*;  | 
|---|
 | 4 | +import timeflow.data.db.*;  | 
|---|
 | 5 | +import timeflow.format.field.*;  | 
|---|
 | 6 | +import timeflow.format.file.DelimitedFormat;  | 
|---|
 | 7 | +import timeflow.model.*;  | 
|---|
 | 8 | +  | 
|---|
 | 9 | +import timeflow.util.*;  | 
|---|
 | 10 | +  | 
|---|
 | 11 | +import javax.swing.*;  | 
|---|
 | 12 | +  | 
|---|
 | 13 | +import java.awt.*;  | 
|---|
 | 14 | +import java.awt.event.*;  | 
|---|
 | 15 | +import java.text.ParseException;  | 
|---|
 | 16 | +import java.util.*;  | 
|---|
 | 17 | +import java.io.*;  | 
|---|
 | 18 | +import java.net.MalformedURLException;  | 
|---|
 | 19 | +import java.net.URL;  | 
|---|
 | 20 | +  | 
|---|
 | 21 | +public class ImportDelimitedPanel extends JFrame   | 
|---|
 | 22 | +{  | 
|---|
 | 23 | +	String fileName;  | 
|---|
 | 24 | +	SchemaPanel schemaPanel;  | 
|---|
 | 25 | +	boolean exitOnClose=false; // for testing!  | 
|---|
 | 26 | +	JScrollPane scroller;  | 
|---|
 | 27 | +	TFModel model;  | 
|---|
 | 28 | +	JLabel numLinesLabel=new JLabel();  | 
|---|
 | 29 | +	  | 
|---|
 | 30 | +	// for testing:  | 
|---|
 | 31 | +	public static void main(String[] args) throws Exception  | 
|---|
 | 32 | +	{  | 
|---|
 | 33 | +		System.out.println("Starting test of ImportEditor");  | 
|---|
 | 34 | +		String file="data/probate.tsv";  | 
|---|
 | 35 | +		String[][] data=DelimitedFormat.readArrayGuessDelim(file, System.out);  | 
|---|
 | 36 | +		ImportDelimitedPanel editor=new ImportDelimitedPanel(new TFModel());  | 
|---|
 | 37 | +		editor.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  | 
|---|
 | 38 | +		editor.setFileName(file);  | 
|---|
 | 39 | +		editor.setData(data);  | 
|---|
 | 40 | +		editor.setBounds(50,50,900,800);  | 
|---|
 | 41 | +		editor.setVisible(true);  | 
|---|
 | 42 | +		editor.exitOnClose=true;  | 
|---|
 | 43 | +	}  | 
|---|
 | 44 | +	  | 
|---|
 | 45 | +	public ImportDelimitedPanel(final TFModel model)  | 
|---|
 | 46 | +	{  | 
|---|
 | 47 | +		super("Import File");  | 
|---|
 | 48 | +		this.model=model;  | 
|---|
 | 49 | +		setBackground(Color.white);  | 
|---|
 | 50 | +		  | 
|---|
 | 51 | +		setLayout(new BorderLayout());  | 
|---|
 | 52 | +		JPanel top=new JPanel();  | 
|---|
 | 53 | +		add(top, BorderLayout.NORTH);  | 
|---|
 | 54 | +		top.setLayout(new FlowLayout(FlowLayout.LEFT));  | 
|---|
 | 55 | +		top.setBackground(Color.lightGray);  | 
|---|
 | 56 | +		top.add(numLinesLabel);  | 
|---|
 | 57 | +		final JTextField source=new JTextField(12);  | 
|---|
 | 58 | +		  | 
|---|
 | 59 | +		JButton done=new JButton("Import This");  | 
|---|
 | 60 | +		top.add(done);  | 
|---|
 | 61 | +		done.addActionListener(new ActionListener() {  | 
|---|
 | 62 | +			@Override  | 
|---|
 | 63 | +			public void actionPerformed(ActionEvent e) {  | 
|---|
 | 64 | +				model.setDB(schemaPanel.makeDB(source.getText()), fileName, true, this);  | 
|---|
 | 65 | +				setVisible(false);  | 
|---|
 | 66 | +				if (exitOnClose)  | 
|---|
 | 67 | +					System.exit(0);  | 
|---|
 | 68 | +			}});  | 
|---|
 | 69 | +		  | 
|---|
 | 70 | +		JButton cancel=new JButton("Cancel");  | 
|---|
 | 71 | +		top.add(cancel);  | 
|---|
 | 72 | +		cancel.addActionListener(new ActionListener() {  | 
|---|
 | 73 | +			@Override  | 
|---|
 | 74 | +			public void actionPerformed(ActionEvent e) {  | 
|---|
 | 75 | +				setVisible(false);  | 
|---|
 | 76 | +				if (exitOnClose)  | 
|---|
 | 77 | +					System.exit(0);  | 
|---|
 | 78 | +			}});  | 
|---|
 | 79 | +		  | 
|---|
 | 80 | +		top.add(new JLabel("        Enter A Source:"));		  | 
|---|
 | 81 | +		top.add(source);  | 
|---|
 | 82 | +		schemaPanel=new SchemaPanel();  | 
|---|
 | 83 | +		schemaPanel.setBackground(Color.white);  | 
|---|
 | 84 | +		scroller=new JScrollPane(schemaPanel);  | 
|---|
 | 85 | +		add(scroller, BorderLayout.CENTER);  | 
|---|
 | 86 | +	}  | 
|---|
 | 87 | +  | 
|---|
 | 88 | +	public void scrollToTop()  | 
|---|
 | 89 | +	{  | 
|---|
 | 90 | +		scroller.getViewport().setViewPosition(new Point(0,0));    | 
|---|
 | 91 | +	}  | 
|---|
 | 92 | +	  | 
|---|
 | 93 | +	public void setFileName(String fileName)  | 
|---|
 | 94 | +	{  | 
|---|
 | 95 | +		this.fileName=fileName;  | 
|---|
 | 96 | +	}  | 
|---|
 | 97 | +	  | 
|---|
 | 98 | +	public void setData(String[][] data)  | 
|---|
 | 99 | +	{  | 
|---|
 | 100 | +		numLinesLabel.setText((data.length-1)+" records read.  ");  | 
|---|
 | 101 | +		schemaPanel.display(data);  | 
|---|
 | 102 | +	}  | 
|---|
 | 103 | +  | 
|---|
 | 104 | +	class SchemaPanel extends JPanel  | 
|---|
 | 105 | +	{  | 
|---|
 | 106 | +		int numFields, rows;  | 
|---|
 | 107 | +		String[][] data;  | 
|---|
 | 108 | +		ArrayList<FieldPanel> panels=new ArrayList<FieldPanel>();  | 
|---|
 | 109 | +		  | 
|---|
 | 110 | +		ActDB makeDB(String source)  | 
|---|
 | 111 | +		{  | 
|---|
 | 112 | +			// count number of fields that are not ignored.  | 
|---|
 | 113 | +			int n=0;  | 
|---|
 | 114 | +			for (FieldPanel fp: panels)  | 
|---|
 | 115 | +				if (!fp.ignore.isSelected())  | 
|---|
 | 116 | +					n++;  | 
|---|
 | 117 | +			  | 
|---|
 | 118 | +			Class[] types=new Class[n];  | 
|---|
 | 119 | +			String[] fieldNames=new String[n];  | 
|---|
 | 120 | +			int[] index=new int[n];  | 
|---|
 | 121 | +			if (source.trim().length()==0)  | 
|---|
 | 122 | +				source="[source unspecified]";  | 
|---|
 | 123 | +			int i=0, j=0;  | 
|---|
 | 124 | +			for (FieldPanel fp: panels)  | 
|---|
 | 125 | +			{			  | 
|---|
 | 126 | +				if (!fp.ignore.isSelected())  | 
|---|
 | 127 | +				{  | 
|---|
 | 128 | +					fieldNames[i]=fp.fieldName;  | 
|---|
 | 129 | +					String typeChoice=(String)fp.typeChoices.getSelectedItem();  | 
|---|
 | 130 | +					Class type=FieldFormatCatalog.javaClass(typeChoice);  | 
|---|
 | 131 | +					System.out.println("Type: "+type+" for: "+typeChoice+" from "+fp.fieldName);  | 
|---|
 | 132 | +					types[i]=type;  | 
|---|
 | 133 | +					index[i]=j;  | 
|---|
 | 134 | +					i++;  | 
|---|
 | 135 | +				}  | 
|---|
 | 136 | +				j++;  | 
|---|
 | 137 | +			}  | 
|---|
 | 138 | +			  | 
|---|
 | 139 | +			ActDB db= new ArrayDB(fieldNames, types, source);  | 
|---|
 | 140 | +			HashMap<Integer, StringBuffer> errors=new HashMap<Integer, StringBuffer>();  | 
|---|
 | 141 | +			for (i=1; i<data.length; i++)  | 
|---|
 | 142 | +			{  | 
|---|
 | 143 | +				Act act=db.createAct();				  | 
|---|
 | 144 | +				for (int k=0; k<n; k++)  | 
|---|
 | 145 | +				{  | 
|---|
 | 146 | +					j=index[k];  | 
|---|
 | 147 | +					String s=data[i][j];  | 
|---|
 | 148 | +					Field f=db.getField(fieldNames[k]);  | 
|---|
 | 149 | +					FieldFormat format=FieldFormatCatalog.getFormat(types[k]);  | 
|---|
 | 150 | +					try  | 
|---|
 | 151 | +					{  | 
|---|
 | 152 | +						Object o=format.parse(s);  | 
|---|
 | 153 | +						act.set(f,o);  | 
|---|
 | 154 | +					}  | 
|---|
 | 155 | +					catch (Exception e)  | 
|---|
 | 156 | +					{  | 
|---|
 | 157 | +						StringBuffer b=errors.get(i-1);  | 
|---|
 | 158 | +						if (b==null)  | 
|---|
 | 159 | +						{  | 
|---|
 | 160 | +							b=new StringBuffer();  | 
|---|
 | 161 | +							errors.put(i-1,b);  | 
|---|
 | 162 | +						}  | 
|---|
 | 163 | +						else  | 
|---|
 | 164 | +							b.append("; ");  | 
|---|
 | 165 | +						b.append(f.getName()+":"+s);  | 
|---|
 | 166 | +					}  | 
|---|
 | 167 | +				}  | 
|---|
 | 168 | +			}  | 
|---|
 | 169 | +			  | 
|---|
 | 170 | +			if (errors.size()>0)  | 
|---|
 | 171 | +			{  | 
|---|
 | 172 | +				Field error=db.addField("UNPARSED FIELDS", String.class);  | 
|---|
 | 173 | +				for (int row:errors.keySet())  | 
|---|
 | 174 | +				{  | 
|---|
 | 175 | +					db.get(row).set(error, errors.get(row).toString());  | 
|---|
 | 176 | +				}  | 
|---|
 | 177 | +			}  | 
|---|
 | 178 | +			  | 
|---|
 | 179 | +			for (j=0; j<n; j++)  | 
|---|
 | 180 | +			{  | 
|---|
 | 181 | +				System.out.println(db.getField(fieldNames[j]));  | 
|---|
 | 182 | +			}  | 
|---|
 | 183 | +			  | 
|---|
 | 184 | +			return db;  | 
|---|
 | 185 | +		}  | 
|---|
 | 186 | +		  | 
|---|
 | 187 | +		void display(String[][] data)  | 
|---|
 | 188 | +		{  | 
|---|
 | 189 | +			removeAll();  | 
|---|
 | 190 | +			  | 
|---|
 | 191 | +			this.data=data;  | 
|---|
 | 192 | +			  | 
|---|
 | 193 | +			// analyze data.  | 
|---|
 | 194 | +			Class[] guesses=FieldFormatGuesser.analyze(data, 1, 100);  | 
|---|
 | 195 | +			  | 
|---|
 | 196 | +			// go through first row, which is headers.  | 
|---|
 | 197 | +			String[] headers=data[0];  | 
|---|
 | 198 | +			  | 
|---|
 | 199 | +			// if there are duplicate headers, add indicators.  | 
|---|
 | 200 | +			HashSet<String> h=new HashSet<String>();  | 
|---|
 | 201 | +			for (int i=0; i<headers.length; i++)  | 
|---|
 | 202 | +			{  | 
|---|
 | 203 | +				String base=headers[i];  | 
|---|
 | 204 | +				int j=2;  | 
|---|
 | 205 | +				String name=base;  | 
|---|
 | 206 | +				while (h.contains(name))  | 
|---|
 | 207 | +				{  | 
|---|
 | 208 | +					name=base+" "+j;  | 
|---|
 | 209 | +					j++;  | 
|---|
 | 210 | +				}  | 
|---|
 | 211 | +				headers[i]=name;  | 
|---|
 | 212 | +				h.add(name);  | 
|---|
 | 213 | +			}  | 
|---|
 | 214 | +			  | 
|---|
 | 215 | +			numFields=headers.length;  | 
|---|
 | 216 | +			int cols=2;  | 
|---|
 | 217 | +			rows=(int)Math.ceil(numFields/2.0);  | 
|---|
 | 218 | +			setLayout(new GridLayout(rows, cols));  | 
|---|
 | 219 | +			for (int i=0; i<numFields; i++)  | 
|---|
 | 220 | +			{  | 
|---|
 | 221 | +				Bag<String> vals=new Bag<String>();  | 
|---|
 | 222 | +				  | 
|---|
 | 223 | +				for (int j=1; j<data.length; j++)  | 
|---|
 | 224 | +				{  | 
|---|
 | 225 | +					vals.add(data[j][i]);  | 
|---|
 | 226 | +				}  | 
|---|
 | 227 | +				java.util.List<String> top=vals.listTop(5);  | 
|---|
 | 228 | +				int n=top.size();  | 
|---|
 | 229 | +				String[] samples=new String[n];  | 
|---|
 | 230 | +				for (int j=0; j<n; j++)  | 
|---|
 | 231 | +				{  | 
|---|
 | 232 | +					String s=top.get(j);  | 
|---|
 | 233 | +					samples[j]=(s.length()==0 ? "*MISSING*" : s)+"    ("+vals.num(s)+" times)";  | 
|---|
 | 234 | +				}  | 
|---|
 | 235 | +				  | 
|---|
 | 236 | +				JPanel p=new JPanel();  | 
|---|
 | 237 | +				add(p);  | 
|---|
 | 238 | +				p.setLayout(new BorderLayout());  | 
|---|
 | 239 | +				FieldPanel f=new FieldPanel(headers[i], samples, guesses[i]);  | 
|---|
 | 240 | +				panels.add(f);  | 
|---|
 | 241 | +				p.add(f, BorderLayout.CENTER);  | 
|---|
 | 242 | +				JPanel hr=new JPanel();  | 
|---|
 | 243 | +				hr.setPreferredSize(new Dimension(20,20));  | 
|---|
 | 244 | +				p.add(hr, BorderLayout.SOUTH);  | 
|---|
 | 245 | +			}  | 
|---|
 | 246 | +		}  | 
|---|
 | 247 | +		  | 
|---|
 | 248 | +		public Dimension getPreferredSize()  | 
|---|
 | 249 | +		{  | 
|---|
 | 250 | +			return new Dimension(400,150*rows);  | 
|---|
 | 251 | +		}  | 
|---|
 | 252 | +	}  | 
|---|
 | 253 | +	  | 
|---|
 | 254 | +	class FieldPanel extends JPanel  | 
|---|
 | 255 | +	{  | 
|---|
 | 256 | +		JComboBox typeChoices;  | 
|---|
 | 257 | +		String fieldName;  | 
|---|
 | 258 | +		JCheckBox ignore;  | 
|---|
 | 259 | +		JLabel fieldLabel;  | 
|---|
 | 260 | +		int x1=5, y1=20, y3=150,x2=150, x3=150, x4=375, dh=2;  | 
|---|
 | 261 | +  | 
|---|
 | 262 | +		  | 
|---|
 | 263 | +		FieldPanel(String fieldName, String[] sampleValues, Class typeGuess)  | 
|---|
 | 264 | +		{  | 
|---|
 | 265 | +			// just going with a null layout here, because it's a lot simpler!  | 
|---|
 | 266 | +			  | 
|---|
 | 267 | +			setLayout(null);  | 
|---|
 | 268 | +			setBackground(Color.white);  | 
|---|
 | 269 | +			this.fieldName=fieldName;  | 
|---|
 | 270 | +			  | 
|---|
 | 271 | +			fieldLabel=new JLabel(" \""+fieldName+'"');	  | 
|---|
 | 272 | +			fieldLabel.setFont(model.getDisplay().big());  | 
|---|
 | 273 | +			add(fieldLabel);  | 
|---|
 | 274 | +			fieldLabel.setBounds(x1,y1,fieldLabel.getPreferredSize().width, fieldLabel.getPreferredSize().height);  | 
|---|
 | 275 | +			  | 
|---|
 | 276 | +			typeChoices=new JComboBox();  | 
|---|
 | 277 | +			for (String choice: FieldFormatCatalog.classNames())  | 
|---|
 | 278 | +				typeChoices.addItem(choice);  | 
|---|
 | 279 | +			typeChoices.setSelectedItem(FieldFormatCatalog.humanName(typeGuess));  | 
|---|
 | 280 | +			add(typeChoices);  | 
|---|
 | 281 | +			int y2=fieldLabel.getY()+fieldLabel.getHeight()+dh+5;  | 
|---|
 | 282 | +			typeChoices.setBounds(x1,y2,  | 
|---|
 | 283 | +					typeChoices.getPreferredSize().width, typeChoices.getPreferredSize().height);  | 
|---|
 | 284 | +				  | 
|---|
 | 285 | +			ignore=new JCheckBox("Ignore Field");			  | 
|---|
 | 286 | +			add(ignore);  | 
|---|
 | 287 | +			ignore.setBounds(x1,typeChoices.getY()+typeChoices.getHeight()+dh,  | 
|---|
 | 288 | +					ignore.getPreferredSize().width, ignore.getPreferredSize().height);  | 
|---|
 | 289 | +			ignore.addActionListener(new ActionListener() {  | 
|---|
 | 290 | +				@Override  | 
|---|
 | 291 | +				public void actionPerformed(ActionEvent e) {  | 
|---|
 | 292 | +					Color c=ignore.isSelected() ? Color.gray : Color.black;  | 
|---|
 | 293 | +					fieldLabel.setForeground(c);  | 
|---|
 | 294 | +					typeChoices.setForeground(c);  | 
|---|
 | 295 | +				}});  | 
|---|
 | 296 | +			  | 
|---|
 | 297 | +			JTextArea values=new JTextArea();  | 
|---|
 | 298 | +			values.setForeground(Color.gray);  | 
|---|
 | 299 | +			for (int i=0; i<sampleValues.length; i++)  | 
|---|
 | 300 | +				values.append(sampleValues[i]+"\n");  | 
|---|
 | 301 | +			add(values);  | 
|---|
 | 302 | +			values.setBounds(x3,y2,x4-x3,y3-y2);  | 
|---|
 | 303 | +		}  | 
|---|
 | 304 | +		  | 
|---|
 | 305 | +		public Dimension getPreferredSize()  | 
|---|
 | 306 | +		{  | 
|---|
 | 307 | +			return new Dimension(500,150);  | 
|---|
 | 308 | +		}  | 
|---|
 | 309 | +	}  | 
|---|
 | 310 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.ui;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import javax.swing.*;  | 
|---|
 | 4 | +import javax.swing.event.ChangeListener;  | 
|---|
 | 5 | +  | 
|---|
 | 6 | +import java.awt.*;  | 
|---|
 | 7 | +import java.awt.event.*;  | 
|---|
 | 8 | +import java.util.*;  | 
|---|
 | 9 | +  | 
|---|
 | 10 | +// custom JTabbedPane-like thing.  | 
|---|
 | 11 | +public class LinkTabPane extends JPanel {  | 
|---|
 | 12 | +	  | 
|---|
 | 13 | +	ArrayList<String> tabNames=new ArrayList<String>();  | 
|---|
 | 14 | +	HashMap<String, JComponent> tabMap=new HashMap<String, JComponent>();  | 
|---|
 | 15 | +	String currentName;  | 
|---|
 | 16 | +	CardLayout cards=new CardLayout();  | 
|---|
 | 17 | +	JPanel center=new JPanel();  | 
|---|
 | 18 | +	LinkTop top=new LinkTop();  | 
|---|
 | 19 | +	  | 
|---|
 | 20 | +	public LinkTabPane()  | 
|---|
 | 21 | +	{  | 
|---|
 | 22 | +		setBackground(Color.white);  | 
|---|
 | 23 | +		setLayout(new BorderLayout());  | 
|---|
 | 24 | +		add(top, BorderLayout.NORTH);  | 
|---|
 | 25 | +		add(center, BorderLayout.CENTER);  | 
|---|
 | 26 | +		center.setLayout(cards);  | 
|---|
 | 27 | +		top.addMouseListener(new MouseAdapter() {  | 
|---|
 | 28 | +			@Override  | 
|---|
 | 29 | +			public void mousePressed(MouseEvent e) {  | 
|---|
 | 30 | +				String s=top.getName(e.getX());  | 
|---|
 | 31 | +				if (s!=null)  | 
|---|
 | 32 | +				{  | 
|---|
 | 33 | +					String old=currentName;  | 
|---|
 | 34 | +					setCurrentName(s);  | 
|---|
 | 35 | +					firePropertyChange("tab", old, s);  | 
|---|
 | 36 | +				}  | 
|---|
 | 37 | +			}});  | 
|---|
 | 38 | +	}  | 
|---|
 | 39 | +	  | 
|---|
 | 40 | +	public String getTitleAt(int i)  | 
|---|
 | 41 | +	{  | 
|---|
 | 42 | +		return tabNames.get(i);  | 
|---|
 | 43 | +	}  | 
|---|
 | 44 | +	  | 
|---|
 | 45 | +	public void setSelectedIndex(int i)  | 
|---|
 | 46 | +	{  | 
|---|
 | 47 | +		setCurrentName(getTitleAt(i));  | 
|---|
 | 48 | +	}  | 
|---|
 | 49 | +	  | 
|---|
 | 50 | +	public void addTab(JComponent component, String name, boolean left)  | 
|---|
 | 51 | +	{  | 
|---|
 | 52 | +		tabNames.add(name);  | 
|---|
 | 53 | +		tabMap.put(name, component);  | 
|---|
 | 54 | +		center.add(component, name);  | 
|---|
 | 55 | +		top.addName(name, left);  | 
|---|
 | 56 | +		repaint();  | 
|---|
 | 57 | +		if (currentName==null)  | 
|---|
 | 58 | +			currentName=name;  | 
|---|
 | 59 | +	}  | 
|---|
 | 60 | +	  | 
|---|
 | 61 | +	public String getCurrentName()  | 
|---|
 | 62 | +	{  | 
|---|
 | 63 | +		return currentName;  | 
|---|
 | 64 | +	}  | 
|---|
 | 65 | +	  | 
|---|
 | 66 | +	public void setCurrentName(final String currentName)  | 
|---|
 | 67 | +	{  | 
|---|
 | 68 | +		this.currentName=currentName;  | 
|---|
 | 69 | +		top.repaint();  | 
|---|
 | 70 | +		SwingUtilities.invokeLater(new Runnable() {public void run() {cards.show(center, currentName);}});  | 
|---|
 | 71 | +  | 
|---|
 | 72 | +	}  | 
|---|
 | 73 | +	  | 
|---|
 | 74 | +	class LinkTop extends JPanel  | 
|---|
 | 75 | +	{  | 
|---|
 | 76 | +		int left, right;  | 
|---|
 | 77 | +		ArrayList<HotLink> leftHots=new ArrayList<HotLink>();  | 
|---|
 | 78 | +		ArrayList<HotLink> rightHots=new ArrayList<HotLink>();  | 
|---|
 | 79 | +		Font font=new Font("Verdana", Font.PLAIN, 14);  | 
|---|
 | 80 | +		FontMetrics fm=getFontMetrics(font);  | 
|---|
 | 81 | +		  | 
|---|
 | 82 | +		LinkTop()  | 
|---|
 | 83 | +		{  | 
|---|
 | 84 | +			setLayout(new FlowLayout(FlowLayout.LEFT));  | 
|---|
 | 85 | +			setBackground(new Color(220,220,220));  | 
|---|
 | 86 | +		}  | 
|---|
 | 87 | +		  | 
|---|
 | 88 | +		public void paintComponent(Graphics g1)  | 
|---|
 | 89 | +		{  | 
|---|
 | 90 | +			int w=getSize().width, h=getSize().height;  | 
|---|
 | 91 | +			Graphics2D g=(Graphics2D)g1;  | 
|---|
 | 92 | +			g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);  | 
|---|
 | 93 | +			g.setColor(getBackground());  | 
|---|
 | 94 | +			g.fillRect(0,0,w,h);  | 
|---|
 | 95 | +			g.setColor(Color.gray);  | 
|---|
 | 96 | +			for (int i=0; i<2; i++)  | 
|---|
 | 97 | +			{  | 
|---|
 | 98 | +				g.drawLine(0,i,w,i);  | 
|---|
 | 99 | +				g.drawLine(0,h-1-i,w,h-1-i);  | 
|---|
 | 100 | +			}  | 
|---|
 | 101 | +			  | 
|---|
 | 102 | +			for (HotLink hot: leftHots)  | 
|---|
 | 103 | +			{  | 
|---|
 | 104 | +				draw(g, hot, h, 0);  | 
|---|
 | 105 | +			}  | 
|---|
 | 106 | +			  | 
|---|
 | 107 | +			for (HotLink hot: rightHots)  | 
|---|
 | 108 | +			{  | 
|---|
 | 109 | +				draw(g, hot, h, w);  | 
|---|
 | 110 | +			}  | 
|---|
 | 111 | +			  | 
|---|
 | 112 | +			for (int i=0; i<leftHots.size(); i++)  | 
|---|
 | 113 | +			{  | 
|---|
 | 114 | +				  | 
|---|
 | 115 | +				if (i<leftHots.size()-1)  | 
|---|
 | 116 | +				{  | 
|---|
 | 117 | +					HotLink hot=leftHots.get(i);  | 
|---|
 | 118 | +					for (int j=0; j<1; j++)  | 
|---|
 | 119 | +						g.drawLine(hot.x+hot.width-1-j, 7, hot.x+hot.width-1-j, h-7);  | 
|---|
 | 120 | +				}  | 
|---|
 | 121 | +			}  | 
|---|
 | 122 | +			  | 
|---|
 | 123 | +			for (int i=0; i<rightHots.size(); i++)  | 
|---|
 | 124 | +			{  | 
|---|
 | 125 | +				  | 
|---|
 | 126 | +				if (i<rightHots.size()-1)  | 
|---|
 | 127 | +				{  | 
|---|
 | 128 | +					HotLink hot=rightHots.get(i);  | 
|---|
 | 129 | +					for (int j=0; j<1; j++)  | 
|---|
 | 130 | +						g.drawLine(hot.x+w-1-j, 7, hot.x+w-1-j, h-7);  | 
|---|
 | 131 | +				}  | 
|---|
 | 132 | +			}  | 
|---|
 | 133 | +		}  | 
|---|
 | 134 | +		  | 
|---|
 | 135 | +		void draw(Graphics g, HotLink hot, int h, int dx)  | 
|---|
 | 136 | +		{  | 
|---|
 | 137 | +			int x=hot.x+dx;  | 
|---|
 | 138 | +			if (hot.s.equals(currentName))  | 
|---|
 | 139 | +			{  | 
|---|
 | 140 | +				g.setColor(Color.lightGray);  | 
|---|
 | 141 | +				g.fillRect(x,2,hot.width,h-4);  | 
|---|
 | 142 | +				g.setColor(Color.gray);  | 
|---|
 | 143 | +				g.drawLine(x-1, 0, x-1, h);  | 
|---|
 | 144 | +				g.drawLine(x+hot.width-1, 0, x+hot.width-1, h);  | 
|---|
 | 145 | +			}  | 
|---|
 | 146 | +			g.setColor(Color.darkGray);  | 
|---|
 | 147 | +			g.setFont(font);  | 
|---|
 | 148 | +			int sw=fm.stringWidth(hot.s);  | 
|---|
 | 149 | +			g.drawString(hot.s, x+(hot.width-sw)/2, h-10);  | 
|---|
 | 150 | +			  | 
|---|
 | 151 | +		}  | 
|---|
 | 152 | +		  | 
|---|
 | 153 | +		String getName(int x)  | 
|---|
 | 154 | +		{  | 
|---|
 | 155 | +			for (HotLink h: leftHots)  | 
|---|
 | 156 | +			{  | 
|---|
 | 157 | +				if (h.x<=x && h.x+h.width>x)  | 
|---|
 | 158 | +					return h.s;  | 
|---|
 | 159 | +			}  | 
|---|
 | 160 | +			for (HotLink h: rightHots)  | 
|---|
 | 161 | +			{  | 
|---|
 | 162 | +				int w=getSize().width;  | 
|---|
 | 163 | +				if (h.x+w<=x && h.x+h.width+w>x)  | 
|---|
 | 164 | +					return h.s;  | 
|---|
 | 165 | +			}  | 
|---|
 | 166 | +  | 
|---|
 | 167 | +			if (leftHots.size()>0)  | 
|---|
 | 168 | +				return leftHots.get(leftHots.size()-1).s;  | 
|---|
 | 169 | +			if (rightHots.size()>0)  | 
|---|
 | 170 | +				return rightHots.get(0).s;  | 
|---|
 | 171 | +			return null;  | 
|---|
 | 172 | +		}  | 
|---|
 | 173 | +		  | 
|---|
 | 174 | +	    void addName(String name, boolean leftward)  | 
|---|
 | 175 | +		{  | 
|---|
 | 176 | +	    	if (leftward)  | 
|---|
 | 177 | +	    	{  | 
|---|
 | 178 | +	    		int x=right;  | 
|---|
 | 179 | +		    	int w=fm.stringWidth(name)+24;  | 
|---|
 | 180 | +		    	leftHots.add(new HotLink(name, x, 0, w, 30));  | 
|---|
 | 181 | +		    	right+=w;  | 
|---|
 | 182 | +	    	}  | 
|---|
 | 183 | +	    	else  | 
|---|
 | 184 | +	    	{  | 
|---|
 | 185 | +	    		int x=left;  | 
|---|
 | 186 | +		    	int w=fm.stringWidth(name)+24;  | 
|---|
 | 187 | +		    	rightHots.add(new HotLink(name, x-w, 0, w, 30));  | 
|---|
 | 188 | +		    	left-=w;		  | 
|---|
 | 189 | +	    	}  | 
|---|
 | 190 | +		}  | 
|---|
 | 191 | +	      | 
|---|
 | 192 | +	    class HotLink extends Rectangle  | 
|---|
 | 193 | +	    {  | 
|---|
 | 194 | +	    	String s;  | 
|---|
 | 195 | +	    	HotLink(String s, int x, int y, int w, int h)  | 
|---|
 | 196 | +	    	{  | 
|---|
 | 197 | +	    		super(x,y,w,h);  | 
|---|
 | 198 | +	    		this.s=s;  | 
|---|
 | 199 | +	    	}  | 
|---|
 | 200 | +	    }  | 
|---|
 | 201 | +		  | 
|---|
 | 202 | +		public Dimension getPreferredSize()  | 
|---|
 | 203 | +		{  | 
|---|
 | 204 | +			return new Dimension(30,30);  | 
|---|
 | 205 | +		}  | 
|---|
 | 206 | +	}  | 
|---|
 | 207 | +	  | 
|---|
 | 208 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.ui;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.data.db.*;  | 
|---|
 | 4 | +import timeflow.model.*;  | 
|---|
 | 5 | +import timeflow.views.*;  | 
|---|
 | 6 | +  | 
|---|
 | 7 | +import javax.swing.*;  | 
|---|
 | 8 | +import javax.swing.table.*;  | 
|---|
 | 9 | +  | 
|---|
 | 10 | +import java.awt.*;  | 
|---|
 | 11 | +import java.util.*;  | 
|---|
 | 12 | +  | 
|---|
 | 13 | +public class MassDeletePanel extends ModelPanel  | 
|---|
 | 14 | +{  | 
|---|
 | 15 | +	TableView table;  | 
|---|
 | 16 | +	ActList keepers;  | 
|---|
 | 17 | +	  | 
|---|
 | 18 | +	public MassDeletePanel(TFModel model, ActList keepers, String title)  | 
|---|
 | 19 | +	{  | 
|---|
 | 20 | +		super(model);  | 
|---|
 | 21 | +		this.keepers=keepers;  | 
|---|
 | 22 | +		setLayout(new BorderLayout());  | 
|---|
 | 23 | +		  | 
|---|
 | 24 | +		JPanel top=new JPanel();  | 
|---|
 | 25 | +		top.setLayout(new GridLayout(4,1));  | 
|---|
 | 26 | +		top.add(new JPanel());  | 
|---|
 | 27 | +		top.add(new JLabel(title));  | 
|---|
 | 28 | +		int n=keepers.size();  | 
|---|
 | 29 | +		String message=null;  | 
|---|
 | 30 | +		if (n>1)  | 
|---|
 | 31 | +			message="These are the "+n+" items that will remain.";  | 
|---|
 | 32 | +		else if (n==1)  | 
|---|
 | 33 | +			message="This in the only item that will remain.";  | 
|---|
 | 34 | +		else   | 
|---|
 | 35 | +			message="No items will remain!";  | 
|---|
 | 36 | +		  | 
|---|
 | 37 | +		JLabel instructions=new JLabel(message);  | 
|---|
 | 38 | +		top.add(instructions);  | 
|---|
 | 39 | +		top.add(new JPanel());  | 
|---|
 | 40 | +		add(top, BorderLayout.NORTH);  | 
|---|
 | 41 | +		  | 
|---|
 | 42 | +		table=new TableView(model);  | 
|---|
 | 43 | +		model.removeListener(table);  | 
|---|
 | 44 | +		add(table, BorderLayout.CENTER);  | 
|---|
 | 45 | +		table.setEditable(false);  | 
|---|
 | 46 | +		table.setActs(keepers);  | 
|---|
 | 47 | +	}  | 
|---|
 | 48 | +	  | 
|---|
 | 49 | +	public void applyAction()  | 
|---|
 | 50 | +	{  | 
|---|
 | 51 | +		ActDB db=getModel().getDB();  | 
|---|
 | 52 | +		HashSet<Act> keepSet=new HashSet<Act>();  | 
|---|
 | 53 | +		keepSet.addAll(keepers);  | 
|---|
 | 54 | +  | 
|---|
 | 55 | +		for (Act a: db.all())  | 
|---|
 | 56 | +			if (!keepSet.contains(a))  | 
|---|
 | 57 | +				db.delete(a);  | 
|---|
 | 58 | +		  | 
|---|
 | 59 | +		// we assume the caller will decide what event to fire.  | 
|---|
 | 60 | +	}  | 
|---|
 | 61 | +	  | 
|---|
 | 62 | +	public void detachFromModel()  | 
|---|
 | 63 | +	{  | 
|---|
 | 64 | +		TFModel model=getModel();  | 
|---|
 | 65 | +		model.removeListener(table);  | 
|---|
 | 66 | +		model.removeListener(this);  | 
|---|
 | 67 | +	}  | 
|---|
 | 68 | +	  | 
|---|
 | 69 | +	public Dimension getPreferredSize()  | 
|---|
 | 70 | +	{  | 
|---|
 | 71 | +		Dimension d=super.getPreferredSize();  | 
|---|
 | 72 | +		return new Dimension(Math.max(700, d.width), 250);  | 
|---|
 | 73 | +	}  | 
|---|
 | 74 | +  | 
|---|
 | 75 | +	@Override  | 
|---|
 | 76 | +	public void note(TFEvent e) {  | 
|---|
 | 77 | +		// TODO Auto-generated method stub  | 
|---|
 | 78 | +		  | 
|---|
 | 79 | +	}  | 
|---|
 | 80 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.ui;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.data.db.*;  | 
|---|
 | 4 | +import timeflow.model.*;  | 
|---|
 | 5 | +import timeflow.views.*;  | 
|---|
 | 6 | +  | 
|---|
 | 7 | +import javax.swing.*;  | 
|---|
 | 8 | +import javax.swing.table.*;  | 
|---|
 | 9 | +  | 
|---|
 | 10 | +import java.awt.*;  | 
|---|
 | 11 | +import java.util.*;  | 
|---|
 | 12 | +  | 
|---|
 | 13 | +public class ReorderFieldsPanel extends ModelPanel  | 
|---|
 | 14 | +{  | 
|---|
 | 15 | +	TableView table;  | 
|---|
 | 16 | +	  | 
|---|
 | 17 | +	public ReorderFieldsPanel(TFModel model)  | 
|---|
 | 18 | +	{  | 
|---|
 | 19 | +		super(model);  | 
|---|
 | 20 | +		setLayout(new BorderLayout());  | 
|---|
 | 21 | +		  | 
|---|
 | 22 | +		JPanel top=new JPanel();  | 
|---|
 | 23 | +		top.setLayout(new GridLayout(3,1));  | 
|---|
 | 24 | +		top.add(new JPanel());  | 
|---|
 | 25 | +		JLabel instructions=new JLabel("Drag and drop the column headers to reorder fields.");  | 
|---|
 | 26 | +		top.add(instructions);  | 
|---|
 | 27 | +		top.add(new JPanel());  | 
|---|
 | 28 | +		add(top, BorderLayout.NORTH);  | 
|---|
 | 29 | +		  | 
|---|
 | 30 | +		table=new TableView(model);  | 
|---|
 | 31 | +		add(table, BorderLayout.CENTER);  | 
|---|
 | 32 | +		table.setEditable(false);  | 
|---|
 | 33 | +		table.setReorderable(true);  | 
|---|
 | 34 | +		table.onscreen(true);  | 
|---|
 | 35 | +	}  | 
|---|
 | 36 | +	  | 
|---|
 | 37 | +	public void applyReordering()  | 
|---|
 | 38 | +	{  | 
|---|
 | 39 | +		Enumeration<TableColumn> columns=table.getTable().getTableHeader().getColumnModel().getColumns();  | 
|---|
 | 40 | +		ArrayList<Field> newOrder=new ArrayList<Field>();  | 
|---|
 | 41 | +		while (columns.hasMoreElements())  | 
|---|
 | 42 | +		{  | 
|---|
 | 43 | +			TableColumn col=columns.nextElement();  | 
|---|
 | 44 | +			String name=col.getHeaderValue().toString();  | 
|---|
 | 45 | +			newOrder.add(getModel().getDB().getField(name));  | 
|---|
 | 46 | +		}  | 
|---|
 | 47 | +		getModel().getDB().setNewFieldOrder(newOrder);  | 
|---|
 | 48 | +	}  | 
|---|
 | 49 | +	  | 
|---|
 | 50 | +	public void detachFromModel()  | 
|---|
 | 51 | +	{  | 
|---|
 | 52 | +		TFModel model=getModel();  | 
|---|
 | 53 | +		model.removeListener(table);  | 
|---|
 | 54 | +		model.removeListener(this);  | 
|---|
 | 55 | +	}  | 
|---|
 | 56 | +	  | 
|---|
 | 57 | +	public Dimension getPreferredSize()  | 
|---|
 | 58 | +	{  | 
|---|
 | 59 | +		Dimension d=super.getPreferredSize();  | 
|---|
 | 60 | +		return new Dimension(Math.max(700, d.width), 250);  | 
|---|
 | 61 | +	}  | 
|---|
 | 62 | +  | 
|---|
 | 63 | +	@Override  | 
|---|
 | 64 | +	public void note(TFEvent e) {  | 
|---|
 | 65 | +		// TODO Auto-generated method stub  | 
|---|
 | 66 | +		  | 
|---|
 | 67 | +	}  | 
|---|
 | 68 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.ui;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.model.*;  | 
|---|
 | 4 | +import timeflow.data.db.*;  | 
|---|
 | 5 | +import timeflow.data.time.*;  | 
|---|
 | 6 | +  | 
|---|
 | 7 | +import timeflow.util.*;  | 
|---|
 | 8 | +  | 
|---|
 | 9 | +import java.awt.*;  | 
|---|
 | 10 | +import java.awt.geom.AffineTransform;  | 
|---|
 | 11 | +  | 
|---|
 | 12 | +public class SizeLegendPanel extends ModelPanel {  | 
|---|
 | 13 | +	Field sizeField;  | 
|---|
 | 14 | +	double min, max;  | 
|---|
 | 15 | +  | 
|---|
 | 16 | +	public SizeLegendPanel(TFModel model)  | 
|---|
 | 17 | +	{  | 
|---|
 | 18 | +		super(model);  | 
|---|
 | 19 | +		setBackground(Color.white);  | 
|---|
 | 20 | +	}  | 
|---|
 | 21 | +	  | 
|---|
 | 22 | +	@Override  | 
|---|
 | 23 | +	public void note(TFEvent e) {  | 
|---|
 | 24 | +		Field size=getModel().getDB().getField(VirtualField.SIZE);  | 
|---|
 | 25 | +		  | 
|---|
 | 26 | +		if (size!=null && (size!=sizeField || e.affectsData()))  | 
|---|
 | 27 | +		{  | 
|---|
 | 28 | +			double[] minmax=DBUtils.minmax(getModel().getActs(), size);  | 
|---|
 | 29 | +			min=minmax[0];  | 
|---|
 | 30 | +			max=minmax[1];  | 
|---|
 | 31 | +		}  | 
|---|
 | 32 | +		sizeField=size;		  | 
|---|
 | 33 | +		repaint();  | 
|---|
 | 34 | +	}  | 
|---|
 | 35 | +	  | 
|---|
 | 36 | +	public Dimension getPreferredSize()  | 
|---|
 | 37 | +	{  | 
|---|
 | 38 | +		return new Dimension(200,40);  | 
|---|
 | 39 | +	}  | 
|---|
 | 40 | +	  | 
|---|
 | 41 | +	public void paintComponent(Graphics g1)  | 
|---|
 | 42 | +	{  | 
|---|
 | 43 | +		Graphics2D g=(Graphics2D)g1;  | 
|---|
 | 44 | +		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);  | 
|---|
 | 45 | +		int w=getSize().width;  | 
|---|
 | 46 | +		int h=getSize().height;  | 
|---|
 | 47 | +		TFModel model=getModel();  | 
|---|
 | 48 | +		Display display=model.getDisplay();  | 
|---|
 | 49 | +		g.setColor(getBackground());  | 
|---|
 | 50 | +		g.setFont(display.plain());  | 
|---|
 | 51 | +		g.fillRect(0,0,w,h);  | 
|---|
 | 52 | +		g.setColor(Color.gray);  | 
|---|
 | 53 | +		  | 
|---|
 | 54 | +		if (sizeField==null)  | 
|---|
 | 55 | +		{  | 
|---|
 | 56 | +			return;  | 
|---|
 | 57 | +		}  | 
|---|
 | 58 | +		else if (Double.isNaN(min))  | 
|---|
 | 59 | +		{  | 
|---|
 | 60 | +			g.drawString("All values missing.",3,20);  | 
|---|
 | 61 | +			return;  | 
|---|
 | 62 | +		}  | 
|---|
 | 63 | +		else  | 
|---|
 | 64 | +		{  | 
|---|
 | 65 | +			AffineTransform old=g.getTransform();  | 
|---|
 | 66 | +			g.setTransform(AffineTransform.getTranslateInstance(20, 0));  | 
|---|
 | 67 | +			if (min==max)  | 
|---|
 | 68 | +			{  | 
|---|
 | 69 | +				g.setColor(Color.gray);  | 
|---|
 | 70 | +				g.fillOval(3,h/2-3,6,6);  | 
|---|
 | 71 | +				g.setColor(Color.black);  | 
|---|
 | 72 | +				g.setFont(display.tiny());  | 
|---|
 | 73 | +				g.drawString(format(min),12,h/2+5);				  | 
|---|
 | 74 | +			}  | 
|---|
 | 75 | +			else  | 
|---|
 | 76 | +			{  | 
|---|
 | 77 | +				String leftLabel=format(min);  | 
|---|
 | 78 | +				String rightLabel=format(max);  | 
|---|
 | 79 | +				g.setFont(display.tiny());  | 
|---|
 | 80 | +				int lw=display.tinyFontMetrics().stringWidth(leftLabel);  | 
|---|
 | 81 | +				int rw=display.tinyFontMetrics().stringWidth(rightLabel);  | 
|---|
 | 82 | +				g.setColor(Color.black);  | 
|---|
 | 83 | +				int ty=h/2+5;;  | 
|---|
 | 84 | +				g.drawString(leftLabel,2,ty);  | 
|---|
 | 85 | +				g.setColor(Color.lightGray);  | 
|---|
 | 86 | +				double maxAbs=Math.max(Math.abs(min), Math.abs(max));  | 
|---|
 | 87 | +				int dx=8+lw;  | 
|---|
 | 88 | +				for (int i=0; i<5; i++)  | 
|---|
 | 89 | +				{  | 
|---|
 | 90 | +					double z=(i*max+(4-i)*min)/4;  | 
|---|
 | 91 | +					int r=(int)(Math.sqrt(Math.abs(z/maxAbs))*Display.MAX_DOT_SIZE);  | 
|---|
 | 92 | +					if (r<1)  | 
|---|
 | 93 | +						r=1;  | 
|---|
 | 94 | +					if (z>0)  | 
|---|
 | 95 | +						g.fillOval(dx,h/2-r,2*r,2*r);  | 
|---|
 | 96 | +					else  | 
|---|
 | 97 | +						g.drawOval(dx,h/2-r,2*r,2*r);  | 
|---|
 | 98 | +					dx+=5+2*r;  | 
|---|
 | 99 | +				}  | 
|---|
 | 100 | +				g.setColor(Color.black);  | 
|---|
 | 101 | +				g.drawString(rightLabel,dx+4,ty);  | 
|---|
 | 102 | +			}  | 
|---|
 | 103 | +			g.setTransform(old);  | 
|---|
 | 104 | +		}  | 
|---|
 | 105 | +	}  | 
|---|
 | 106 | +	  | 
|---|
 | 107 | +	String format(double x)  | 
|---|
 | 108 | +	{  | 
|---|
 | 109 | +		if (Math.abs(x)>10 && (max-min)>10)  | 
|---|
 | 110 | +			return Display.format(Math.round(x));  | 
|---|
 | 111 | +		return Display.format(x);  | 
|---|
 | 112 | +	}  | 
|---|
 | 113 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.ui;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.model.*;  | 
|---|
 | 4 | +import timeflow.app.ui.filter.FilterControlPanel;  | 
|---|
 | 5 | +import timeflow.data.db.*;  | 
|---|
 | 6 | +import timeflow.data.db.filter.ActFilter;  | 
|---|
 | 7 | +  | 
|---|
 | 8 | +import javax.swing.*;  | 
|---|
 | 9 | +import java.awt.*;  | 
|---|
 | 10 | +import java.awt.event.ActionEvent;  | 
|---|
 | 11 | +import java.awt.event.ActionListener;  | 
|---|
 | 12 | +import java.text.*;  | 
|---|
 | 13 | +  | 
|---|
 | 14 | +public class StatusPanel extends ModelPanel  | 
|---|
 | 15 | +{  | 
|---|
 | 16 | +	JLabel numLabel=new JLabel("")  | 
|---|
 | 17 | +	{  | 
|---|
 | 18 | +		public Dimension getPreferredSize()  | 
|---|
 | 19 | +		{  | 
|---|
 | 20 | +			return new Dimension(30,25);  | 
|---|
 | 21 | +		}  | 
|---|
 | 22 | +	};  | 
|---|
 | 23 | +	JLabel filterLabel=new JLabel("")  | 
|---|
 | 24 | +	{  | 
|---|
 | 25 | +		public Dimension getPreferredSize()  | 
|---|
 | 26 | +		{  | 
|---|
 | 27 | +			return new Dimension(30,25);  | 
|---|
 | 28 | +		}  | 
|---|
 | 29 | +	};  | 
|---|
 | 30 | +	  | 
|---|
 | 31 | +	static final DecimalFormat niceFormat=new DecimalFormat("###,###");  | 
|---|
 | 32 | +	  | 
|---|
 | 33 | +	public StatusPanel(TFModel model, final FilterControlPanel filterControls) {  | 
|---|
 | 34 | +		super(model);	  | 
|---|
 | 35 | +		setLayout(new BorderLayout());  | 
|---|
 | 36 | +		setBackground(new Color(245, 245, 245));  | 
|---|
 | 37 | +		setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 15));  | 
|---|
 | 38 | +		JPanel center=new JPanel();  | 
|---|
 | 39 | +		center.setBackground(getBackground());  | 
|---|
 | 40 | +		center.setLayout(new GridLayout(2,1));  | 
|---|
 | 41 | +		add(center, BorderLayout.CENTER);  | 
|---|
 | 42 | +		  | 
|---|
 | 43 | +		center.add(numLabel);  | 
|---|
 | 44 | +		numLabel.setFont(model.getDisplay().plain());  | 
|---|
 | 45 | +		numLabel.setBackground(new Color(245, 245, 245));  | 
|---|
 | 46 | +		  | 
|---|
 | 47 | +		JPanel bottom=new JPanel();  | 
|---|
 | 48 | +		center.add(bottom);  | 
|---|
 | 49 | +		bottom.setLayout(new BorderLayout());  | 
|---|
 | 50 | +		bottom.add(filterLabel, BorderLayout.CENTER);  | 
|---|
 | 51 | +		bottom.setBackground(new Color(245, 245, 245));  | 
|---|
 | 52 | +		filterLabel.setFont(model.getDisplay().plain());  | 
|---|
 | 53 | +		filterLabel.setBackground(new Color(245, 245, 245));  | 
|---|
 | 54 | +		filterLabel.setForeground(Color.red);  | 
|---|
 | 55 | +		  | 
|---|
 | 56 | +		JPanel clearPanel=new JPanel();  | 
|---|
 | 57 | +		clearPanel.setBackground(new Color(245, 245, 245));  | 
|---|
 | 58 | +		clearPanel.setLayout(new GridLayout(1,1));  | 
|---|
 | 59 | +		JButton clear=new JButton(new ImageIcon("images/button_clear_all.gif"));  | 
|---|
 | 60 | +		clear.setBorder(null);  | 
|---|
 | 61 | +		clear.addActionListener(new ActionListener() {  | 
|---|
 | 62 | +			@Override  | 
|---|
 | 63 | +			public void actionPerformed(ActionEvent e) {  | 
|---|
 | 64 | +				filterControls.clearFilters();  | 
|---|
 | 65 | +			}  | 
|---|
 | 66 | +		});  | 
|---|
 | 67 | +		clearPanel.add(clear);  | 
|---|
 | 68 | +		bottom.add(clearPanel, BorderLayout.EAST);  | 
|---|
 | 69 | +		  | 
|---|
 | 70 | +		add(new DottedLine(), BorderLayout.SOUTH);  | 
|---|
 | 71 | +	}  | 
|---|
 | 72 | +  | 
|---|
 | 73 | +	@Override  | 
|---|
 | 74 | +	public void note(TFEvent e) {  | 
|---|
 | 75 | +		  | 
|---|
 | 76 | +		ActDB db=getModel().getDB();  | 
|---|
 | 77 | +		if (db==null || db.size()==0)  | 
|---|
 | 78 | +		{  | 
|---|
 | 79 | +			numLabel.setForeground(new Color(245,245,245));//Color.gray);  | 
|---|
 | 80 | +			numLabel.setText("No data");  | 
|---|
 | 81 | +			return;  | 
|---|
 | 82 | +		}  | 
|---|
 | 83 | +		int numTotal=db.size();  | 
|---|
 | 84 | +		ActList acts=getModel().getActs();  | 
|---|
 | 85 | +		int numShown=acts.size();  | 
|---|
 | 86 | +		filterLabel.setText(numShown<numTotal ? " Filters applied" : " Not Filtering");  | 
|---|
 | 87 | +		filterLabel.setForeground(numShown==numTotal ? Color.lightGray : Color.red);  | 
|---|
 | 88 | +		numLabel.setForeground(numShown==0 ? Color.red : Color.darkGray);  | 
|---|
 | 89 | +		String plural=(numTotal==1 ? "" : "s");  | 
|---|
 | 90 | +		if (numShown==numTotal)  | 
|---|
 | 91 | +			numLabel.setText(" Showing All "+niceFormat.format(numTotal)+" Event"+plural);  | 
|---|
 | 92 | +		else  | 
|---|
 | 93 | +			numLabel.setText(" Showing "+niceFormat.format(numShown)  | 
|---|
 | 94 | +					+" / "+niceFormat.format(numTotal)+" Event"+ plural);  | 
|---|
 | 95 | +		repaint();  | 
|---|
 | 96 | +	}  | 
|---|
 | 97 | +  | 
|---|
 | 98 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.ui;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import javax.swing.*;  | 
|---|
 | 4 | +import java.awt.*;  | 
|---|
 | 5 | +import java.awt.event.*;  | 
|---|
 | 6 | +  | 
|---|
 | 7 | +public class WaitingDialog extends JFrame {  | 
|---|
 | 8 | +	  | 
|---|
 | 9 | +	Timer timer;  | 
|---|
 | 10 | +	  | 
|---|
 | 11 | +	public static void main(String[] args)  | 
|---|
 | 12 | +	{  | 
|---|
 | 13 | +		new WaitingDialog("Testing", "Hello, world!");  | 
|---|
 | 14 | +	}  | 
|---|
 | 15 | +		  | 
|---|
 | 16 | +	public WaitingDialog(String title, String message)  | 
|---|
 | 17 | +	{  | 
|---|
 | 18 | +		super(title);  | 
|---|
 | 19 | +		Throbber throbber=new Throbber();  | 
|---|
 | 20 | +		timer=new Timer(50, throbber);		  | 
|---|
 | 21 | +		getContentPane().setLayout(new FlowLayout(FlowLayout.LEFT));  | 
|---|
 | 22 | +		getContentPane().add(throbber);  | 
|---|
 | 23 | +		getContentPane().add(new JLabel(message));  | 
|---|
 | 24 | +		setBounds(400,400,300,150);  | 
|---|
 | 25 | +		setVisible(true);  | 
|---|
 | 26 | +		timer.start();  | 
|---|
 | 27 | +	}  | 
|---|
 | 28 | +	  | 
|---|
 | 29 | +	public void stop()  | 
|---|
 | 30 | +	{  | 
|---|
 | 31 | +		timer.stop();  | 
|---|
 | 32 | +		setVisible(false);  | 
|---|
 | 33 | +	}  | 
|---|
 | 34 | +	  | 
|---|
 | 35 | +	class Throbber extends JPanel implements ActionListener  | 
|---|
 | 36 | +	{  | 
|---|
 | 37 | +		int count=0;  | 
|---|
 | 38 | +		public void paintComponent(Graphics g)  | 
|---|
 | 39 | +		{  | 
|---|
 | 40 | +			int w=getSize().width, h=getSize().height;  | 
|---|
 | 41 | +			int c=count%256;  | 
|---|
 | 42 | +			g.setColor(new Color(c,c,c));  | 
|---|
 | 43 | +			g.fillRect(0,0,w,h);  | 
|---|
 | 44 | +		}  | 
|---|
 | 45 | +		@Override  | 
|---|
 | 46 | +		public void actionPerformed(ActionEvent e) {  | 
|---|
 | 47 | +			repaint();  | 
|---|
 | 48 | +			count+=10;  | 
|---|
 | 49 | +		}  | 
|---|
 | 50 | +		  | 
|---|
 | 51 | +		public Dimension getPreferredSize()  | 
|---|
 | 52 | +		{  | 
|---|
 | 53 | +			return new Dimension(30,100);  | 
|---|
 | 54 | +		}  | 
|---|
 | 55 | +	}  | 
|---|
 | 56 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.ui.filter;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import javax.swing.*;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +import timeflow.data.time.Interval;  | 
|---|
 | 6 | +import timeflow.model.Display;  | 
|---|
 | 7 | +  | 
|---|
 | 8 | +  | 
|---|
 | 9 | +import java.awt.*;  | 
|---|
 | 10 | +import java.awt.event.MouseAdapter;  | 
|---|
 | 11 | +import java.awt.event.MouseEvent;  | 
|---|
 | 12 | +import java.awt.event.MouseMotionAdapter;  | 
|---|
 | 13 | +import java.text.*;  | 
|---|
 | 14 | +  | 
|---|
 | 15 | +  | 
|---|
 | 16 | +public class BabyHistogram extends JPanel {  | 
|---|
 | 17 | +	private int[] buckets;  | 
|---|
 | 18 | +	private double[] x;  | 
|---|
 | 19 | +	private double min, max;  | 
|---|
 | 20 | +	private int numDefined;  | 
|---|
 | 21 | +	private int maxBucket;  | 
|---|
 | 22 | +	private double value;  | 
|---|
 | 23 | +	private static final DecimalFormat df=new DecimalFormat("###,###,###,###.##");  | 
|---|
 | 24 | +	  | 
|---|
 | 25 | +	  | 
|---|
 | 26 | +	Point mouseHit=new Point();  | 
|---|
 | 27 | +	Point mouse=new Point(-1,0);  | 
|---|
 | 28 | +	enum Modify {START, END, POSITION, NONE};  | 
|---|
 | 29 | +	Modify change=Modify.NONE;  | 
|---|
 | 30 | +	Rectangle startRect=new Rectangle(-1,-1,0,0);  | 
|---|
 | 31 | +	Rectangle endRect=new Rectangle(-1,-1,0,0);  | 
|---|
 | 32 | +	Rectangle positionRect=new Rectangle(-1,-1,0,0);  | 
|---|
 | 33 | +	Color sidePlain=Color.orange;  | 
|---|
 | 34 | +	Color sideMouse=new Color(230,100,0);  | 
|---|
 | 35 | +	double relLow=0, relHigh=1, originalLow, originalHigh;  | 
|---|
 | 36 | +	  | 
|---|
 | 37 | +	public void setRelRange(double relLow, double relHigh)  | 
|---|
 | 38 | +	{  | 
|---|
 | 39 | +		this.relLow=Math.max(0,relLow);  | 
|---|
 | 40 | +		this.relHigh=Math.min(1,relHigh);  | 
|---|
 | 41 | +		repaint();  | 
|---|
 | 42 | +	}  | 
|---|
 | 43 | +  | 
|---|
 | 44 | +	public void setTrueRange(double low, double high)  | 
|---|
 | 45 | +	{  | 
|---|
 | 46 | +		double span=max-min;  | 
|---|
 | 47 | +		if (span<=0) // nothing much to do...  | 
|---|
 | 48 | +			return;  | 
|---|
 | 49 | +		  | 
|---|
 | 50 | +		setRelRange((low-min)/span, (high-min)/span);	  | 
|---|
 | 51 | +	}  | 
|---|
 | 52 | +	  | 
|---|
 | 53 | +	public boolean isEverything()  | 
|---|
 | 54 | +	{  | 
|---|
 | 55 | +		return relLow==0 && relHigh==1;  | 
|---|
 | 56 | +	}  | 
|---|
 | 57 | +	  | 
|---|
 | 58 | +	public double getLow()  | 
|---|
 | 59 | +	{  | 
|---|
 | 60 | +		return abs(relLow);  | 
|---|
 | 61 | +	}  | 
|---|
 | 62 | +	  | 
|---|
 | 63 | +	public double getHigh()  | 
|---|
 | 64 | +	{  | 
|---|
 | 65 | +		return abs(relHigh);  | 
|---|
 | 66 | +	}  | 
|---|
 | 67 | +	  | 
|---|
 | 68 | +	private double abs(double x)  | 
|---|
 | 69 | +	{  | 
|---|
 | 70 | +		return x*max+(1-x)*min;  | 
|---|
 | 71 | +	}  | 
|---|
 | 72 | +  | 
|---|
 | 73 | +	public BabyHistogram(final Runnable changeAction)  | 
|---|
 | 74 | +	{  | 
|---|
 | 75 | +		  | 
|---|
 | 76 | +		addMouseListener(new MouseAdapter() {  | 
|---|
 | 77 | +  | 
|---|
 | 78 | +			@Override  | 
|---|
 | 79 | +			public void mousePressed(MouseEvent e) {  | 
|---|
 | 80 | +				int mx=e.getX();  | 
|---|
 | 81 | +				int my=e.getY();  | 
|---|
 | 82 | +				int lx=lowX(), hx=highX();  | 
|---|
 | 83 | +			  | 
|---|
 | 84 | +				int ox=0;  | 
|---|
 | 85 | +				if (Math.abs(mx-lx)<Math.abs(mx-hx))  | 
|---|
 | 86 | +				{  | 
|---|
 | 87 | +					change=Modify.START;  | 
|---|
 | 88 | +					ox=lx;  | 
|---|
 | 89 | +				}  | 
|---|
 | 90 | +				else  | 
|---|
 | 91 | +				{  | 
|---|
 | 92 | +					change=Modify.END;  | 
|---|
 | 93 | +					ox=hx;  | 
|---|
 | 94 | +				}  | 
|---|
 | 95 | +				mouseHit.setLocation(ox,my);  | 
|---|
 | 96 | +				mouse.setLocation(mx,my);  | 
|---|
 | 97 | +				originalLow=relLow;  | 
|---|
 | 98 | +				originalHigh=relHigh;  | 
|---|
 | 99 | +				repaint();  | 
|---|
 | 100 | +			}  | 
|---|
 | 101 | +  | 
|---|
 | 102 | +			@Override  | 
|---|
 | 103 | +			public void mouseReleased(MouseEvent e) {  | 
|---|
 | 104 | +				change=Modify.NONE;  | 
|---|
 | 105 | +				repaint();  | 
|---|
 | 106 | +			}});  | 
|---|
 | 107 | +		addMouseMotionListener(new MouseMotionAdapter() {  | 
|---|
 | 108 | +  | 
|---|
 | 109 | +			@Override  | 
|---|
 | 110 | +			public void mouseDragged(MouseEvent e) {  | 
|---|
 | 111 | +				  | 
|---|
 | 112 | +				if (change==Modify.NONE)  | 
|---|
 | 113 | +					return;  | 
|---|
 | 114 | +				mouse.setLocation(e.getX(), e.getY());  | 
|---|
 | 115 | +				int mouseDiff=mouse.x-mouseHit.x;  | 
|---|
 | 116 | +				double relDiff=mouseDiff/(double)getSize().width;  | 
|---|
 | 117 | +				switch (change)  | 
|---|
 | 118 | +				{  | 
|---|
 | 119 | +					case POSITION: 						  | 
|---|
 | 120 | +							relLow=originalLow+relDiff;  | 
|---|
 | 121 | +							relHigh=originalHigh+relDiff;  | 
|---|
 | 122 | +  | 
|---|
 | 123 | +						break;  | 
|---|
 | 124 | +					case START: relLow=originalLow+relDiff;  | 
|---|
 | 125 | +						break;  | 
|---|
 | 126 | +					case END: relHigh=originalHigh+relDiff;  | 
|---|
 | 127 | +				}  | 
|---|
 | 128 | +				relLow=Math.max(0, relLow);  | 
|---|
 | 129 | +				relHigh=Math.min(1, relHigh);  | 
|---|
 | 130 | +				changeAction.run();  | 
|---|
 | 131 | +				repaint();  | 
|---|
 | 132 | +			}  | 
|---|
 | 133 | +		});  | 
|---|
 | 134 | +  | 
|---|
 | 135 | +	}  | 
|---|
 | 136 | +	  | 
|---|
 | 137 | +	public void setData(double[] x)  | 
|---|
 | 138 | +	{  | 
|---|
 | 139 | +		relLow=0;  | 
|---|
 | 140 | +		relHigh=1;  | 
|---|
 | 141 | +		  | 
|---|
 | 142 | +		this.x=x;  | 
|---|
 | 143 | +		int n=x.length;  | 
|---|
 | 144 | +		  | 
|---|
 | 145 | +		// do some quick checks on the data.  | 
|---|
 | 146 | +		boolean positive=true;  | 
|---|
 | 147 | +		min=Double.NaN;  | 
|---|
 | 148 | +		max=Double.NaN;  | 
|---|
 | 149 | +		numDefined=0;  | 
|---|
 | 150 | +		for (int i=0; i<n; i++)  | 
|---|
 | 151 | +		{  | 
|---|
 | 152 | +			double m=x[i];  | 
|---|
 | 153 | +			if (!Double.isNaN(m))  | 
|---|
 | 154 | +			{  | 
|---|
 | 155 | +				numDefined++;  | 
|---|
 | 156 | +				positive &= m>0;  | 
|---|
 | 157 | +				if (Double.isNaN(min))  | 
|---|
 | 158 | +				{  | 
|---|
 | 159 | +					min=m;  | 
|---|
 | 160 | +					max=m;  | 
|---|
 | 161 | +					value=m;  | 
|---|
 | 162 | +				}  | 
|---|
 | 163 | +				else  | 
|---|
 | 164 | +				{  | 
|---|
 | 165 | +					min=Math.min(m, min);  | 
|---|
 | 166 | +					max=Math.max(m, max);  | 
|---|
 | 167 | +				}  | 
|---|
 | 168 | +			}  | 
|---|
 | 169 | +		}  | 
|---|
 | 170 | +		  | 
|---|
 | 171 | +		if (numDefined==0)  | 
|---|
 | 172 | +			return;  | 
|---|
 | 173 | +		if (min==max)  | 
|---|
 | 174 | +		{  | 
|---|
 | 175 | +			buckets=new int[1];  | 
|---|
 | 176 | +			buckets[0]=numDefined;  | 
|---|
 | 177 | +			maxBucket=numDefined;  | 
|---|
 | 178 | +			return;  | 
|---|
 | 179 | +		}  | 
|---|
 | 180 | +		int numBuckets=(int)Math.min(50, 2*Math.sqrt(numDefined));  | 
|---|
 | 181 | +		buckets=new int[numBuckets];  | 
|---|
 | 182 | +		maxBucket=0;  | 
|---|
 | 183 | +		for (int i=0; i<n; i++)  | 
|---|
 | 184 | +		{  | 
|---|
 | 185 | +			if (!Double.isNaN(x[i]))  | 
|---|
 | 186 | +			{  | 
|---|
 | 187 | +				int b=(int)((numBuckets-1)*(x[i]-min)/(max-min));  | 
|---|
 | 188 | +				buckets[b]++;  | 
|---|
 | 189 | +				maxBucket=Math.max(maxBucket, buckets[b]);  | 
|---|
 | 190 | +			}  | 
|---|
 | 191 | +		}  | 
|---|
 | 192 | +	}  | 
|---|
 | 193 | +	  | 
|---|
 | 194 | +	public void paintComponent(Graphics g)  | 
|---|
 | 195 | +	{  | 
|---|
 | 196 | +		int w=getSize().width;  | 
|---|
 | 197 | +		int h=getSize().height;  | 
|---|
 | 198 | +		g.setColor(Color.white);  | 
|---|
 | 199 | +		g.fillRect(0,0,w,h);  | 
|---|
 | 200 | +		  | 
|---|
 | 201 | +		if (x==null)  | 
|---|
 | 202 | +		{			  | 
|---|
 | 203 | +			say(g, "No data");  | 
|---|
 | 204 | +			return;  | 
|---|
 | 205 | +		}  | 
|---|
 | 206 | +		  | 
|---|
 | 207 | +		if (x.length==0)  | 
|---|
 | 208 | +		{  | 
|---|
 | 209 | +			say(g, "No values");  | 
|---|
 | 210 | +			return;  | 
|---|
 | 211 | +		}  | 
|---|
 | 212 | +		  | 
|---|
 | 213 | +		if (numDefined==0)  | 
|---|
 | 214 | +		{  | 
|---|
 | 215 | +			say(g, "No defined values");  | 
|---|
 | 216 | +			return;  | 
|---|
 | 217 | +		}  | 
|---|
 | 218 | +		  | 
|---|
 | 219 | +		int n=buckets.length;  | 
|---|
 | 220 | +		if (n==1)  | 
|---|
 | 221 | +		{  | 
|---|
 | 222 | +			say(g, "All defined vals = "+df.format(value));  | 
|---|
 | 223 | +			return;  | 
|---|
 | 224 | +		}  | 
|---|
 | 225 | +		  | 
|---|
 | 226 | +		// wow, if we got here we really have a histogram and not a degenerate mess!  | 
|---|
 | 227 | +		  | 
|---|
 | 228 | +		Color bar=Display.barColor;  | 
|---|
 | 229 | +		g.setColor(bar);  | 
|---|
 | 230 | +		for (int i=0; i<n; i++)  | 
|---|
 | 231 | +		{  | 
|---|
 | 232 | +			int x1=(i*w)/n;  | 
|---|
 | 233 | +			int x2=((i+1)*w)/n;  | 
|---|
 | 234 | +			int y1=h-(buckets[i]*h)/maxBucket;  | 
|---|
 | 235 | +			if (buckets[i]>0 && y1>h-2)  | 
|---|
 | 236 | +				y1=h-2;  | 
|---|
 | 237 | +			g.fillRect(x1,y1,x2-x1-1,h-y1);  | 
|---|
 | 238 | +		}  | 
|---|
 | 239 | +		  | 
|---|
 | 240 | +		// now draw thumb.  | 
|---|
 | 241 | +		  | 
|---|
 | 242 | +		int thumb1=lowX();  | 
|---|
 | 243 | +		int thumb2=highX();  | 
|---|
 | 244 | +		g.setColor(Color.black);  | 
|---|
 | 245 | +		g.drawLine(thumb1,0,thumb1,h);  | 
|---|
 | 246 | +		g.drawLine(thumb2,0,thumb2,h);  | 
|---|
 | 247 | +		g.setColor(new Color(235,235,235,160));  | 
|---|
 | 248 | +		g.fillRect(0,0,thumb1,h);  | 
|---|
 | 249 | +		g.fillRect(thumb2+1,0,w-thumb2-1,h);  | 
|---|
 | 250 | +		g.setColor(Color.lightGray);  | 
|---|
 | 251 | +		g.drawRect(0,0,w-1,h-1);  | 
|---|
 | 252 | +	}  | 
|---|
 | 253 | +	  | 
|---|
 | 254 | +	int lowX()  | 
|---|
 | 255 | +	{  | 
|---|
 | 256 | +		return (int)((getSize().width-1)*relLow);  | 
|---|
 | 257 | +	}  | 
|---|
 | 258 | +	  | 
|---|
 | 259 | +	int highX()  | 
|---|
 | 260 | +	{  | 
|---|
 | 261 | +		return (int)((getSize().width-1)*relHigh);  | 
|---|
 | 262 | +	}  | 
|---|
 | 263 | +	  | 
|---|
 | 264 | +	void say(Graphics g, String s)  | 
|---|
 | 265 | +	{  | 
|---|
 | 266 | +		g.setColor(Color.gray);  | 
|---|
 | 267 | +		g.drawString(s,5,getSize().height-5);  | 
|---|
 | 268 | +	}  | 
|---|
 | 269 | +	  | 
|---|
 | 270 | +	public Dimension getPreferredSize()  | 
|---|
 | 271 | +	{  | 
|---|
 | 272 | +		return new Dimension(200,60);  | 
|---|
 | 273 | +	}  | 
|---|
 | 274 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.ui.filter;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.util.*;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +import javax.swing.*;  | 
|---|
 | 6 | +  | 
|---|
 | 7 | +import timeflow.data.db.*;  | 
|---|
 | 8 | +import timeflow.data.db.filter.*;  | 
|---|
 | 9 | +import timeflow.model.ModelPanel;  | 
|---|
 | 10 | +  | 
|---|
 | 11 | +import java.awt.*;  | 
|---|
 | 12 | +import java.awt.event.*;  | 
|---|
 | 13 | +  | 
|---|
 | 14 | +public class FilterCategoryPanel extends FilterDefinitionPanel   | 
|---|
 | 15 | +{  | 
|---|
 | 16 | +	public JList dataList=new JList();  | 
|---|
 | 17 | +	Field field;  | 
|---|
 | 18 | +	  | 
|---|
 | 19 | +	public FilterCategoryPanel(final Field field, final ModelPanel parent)  | 
|---|
 | 20 | +	{  | 
|---|
 | 21 | +		this(field.getName(), field, parent);  | 
|---|
 | 22 | +	}  | 
|---|
 | 23 | +	  | 
|---|
 | 24 | +	public FilterCategoryPanel(String title, final Field field, final ModelPanel parent)  | 
|---|
 | 25 | +	{  | 
|---|
 | 26 | +		this.field=field;  | 
|---|
 | 27 | +		setLayout(new BorderLayout());  | 
|---|
 | 28 | +		setBackground(Color.white);  | 
|---|
 | 29 | +		setBorder(BorderFactory.createEmptyBorder(0,5,0,5));  | 
|---|
 | 30 | +  | 
|---|
 | 31 | +		add(new FilterTitle(title, field, parent, true), BorderLayout.NORTH);  | 
|---|
 | 32 | +		  | 
|---|
 | 33 | +		  | 
|---|
 | 34 | +		JScrollPane scroller=new JScrollPane(dataList);  | 
|---|
 | 35 | +		scroller.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);  | 
|---|
 | 36 | +		scroller.setBorder(null);  | 
|---|
 | 37 | +		add(scroller, BorderLayout.CENTER);  | 
|---|
 | 38 | +		dataList.setForeground(Color.darkGray);  | 
|---|
 | 39 | +		dataList.setSelectionForeground(Color.black);  | 
|---|
 | 40 | +		dataList.setSelectionBackground(new Color(220,235,255));  | 
|---|
 | 41 | +		dataList.setFont(parent.getModel().getDisplay().small());  | 
|---|
 | 42 | +		scroller.getVerticalScrollBar().setBackground(Color.white);  | 
|---|
 | 43 | +		  | 
|---|
 | 44 | +		  | 
|---|
 | 45 | +		// ok, the following is ugly code to insert a new mouselistener  | 
|---|
 | 46 | +		// that lets the user deselect items when they are clicked.  | 
|---|
 | 47 | +		// i tried a bunch of stuff but this is all that would work--  | 
|---|
 | 48 | +		// and searching the web yielded only solutions similar to this.  | 
|---|
 | 49 | +		// also, there's a weird dance with consuming/not consuming events  | 
|---|
 | 50 | +		// that is designed to allow a certain kind of multi-selection behavior  | 
|---|
 | 51 | +		// with the mouse, while letting you scroll through items one at a time  | 
|---|
 | 52 | +		// with the keyboard. this was the product of a long series of  | 
|---|
 | 53 | +		// conversations with target users.  | 
|---|
 | 54 | +		MouseListener[] old = dataList.getMouseListeners();  | 
|---|
 | 55 | +		for (MouseListener m: old)  | 
|---|
 | 56 | +		   dataList.removeMouseListener(m);  | 
|---|
 | 57 | +		  | 
|---|
 | 58 | +		dataList.addMouseListener(new MouseAdapter()  | 
|---|
 | 59 | +		{  | 
|---|
 | 60 | +		   public void mousePressed(MouseEvent e)  | 
|---|
 | 61 | +		   {  | 
|---|
 | 62 | +			  if (e.isControlDown() || e.isMetaDown() || e.isShiftDown())  | 
|---|
 | 63 | +				  return;  | 
|---|
 | 64 | +		      final int index = dataList.locationToIndex(e.getPoint());  | 
|---|
 | 65 | +		      if (dataList.isSelectedIndex(index))  | 
|---|
 | 66 | +		      {  | 
|---|
 | 67 | +		         SwingUtilities.invokeLater(new Runnable()  | 
|---|
 | 68 | +		         {  | 
|---|
 | 69 | +		            public void run()  | 
|---|
 | 70 | +		            {  | 
|---|
 | 71 | +		               dataList.removeSelectionInterval(index, index);  | 
|---|
 | 72 | +		                | 
|---|
 | 73 | +		            }  | 
|---|
 | 74 | +		         });  | 
|---|
 | 75 | +		         e.consume();  | 
|---|
 | 76 | +		      }  | 
|---|
 | 77 | +		      else  | 
|---|
 | 78 | +		      {  | 
|---|
 | 79 | +		    	  SwingUtilities.invokeLater(new Runnable()  | 
|---|
 | 80 | +			         {  | 
|---|
 | 81 | +			            public void run()  | 
|---|
 | 82 | +			            {  | 
|---|
 | 83 | +			            	dataList.addSelectionInterval(index, index);  | 
|---|
 | 84 | +			                | 
|---|
 | 85 | +			            }  | 
|---|
 | 86 | +			         });		    	    | 
|---|
 | 87 | +		    	  e.consume();  | 
|---|
 | 88 | +		      }  | 
|---|
 | 89 | +		   }  | 
|---|
 | 90 | +		});  | 
|---|
 | 91 | +  | 
|---|
 | 92 | +		for (MouseListener m: old)  | 
|---|
 | 93 | +		   dataList.addMouseListener(m);  | 
|---|
 | 94 | +		  | 
|---|
 | 95 | +		dataList.setCellRenderer(new DefaultListCellRenderer() {  | 
|---|
 | 96 | +			@Override  | 
|---|
 | 97 | +			public Component getListCellRendererComponent(JList list,  | 
|---|
 | 98 | +					Object value, int index, boolean isSelected,  | 
|---|
 | 99 | +					boolean cellHasFocus) {  | 
|---|
 | 100 | +				Component c=super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);  | 
|---|
 | 101 | +				if (field==parent.getModel().getColorField())  | 
|---|
 | 102 | +				{  | 
|---|
 | 103 | +					String text=value.toString();  | 
|---|
 | 104 | +					int n=text.lastIndexOf('-');  | 
|---|
 | 105 | +					if (n>1)  | 
|---|
 | 106 | +						text=text.substring(0,n-1);  | 
|---|
 | 107 | +					c.setForeground(parent.getModel().getDisplay().makeColor(text));  | 
|---|
 | 108 | +				}  | 
|---|
 | 109 | +				return c;  | 
|---|
 | 110 | +			}});  | 
|---|
 | 111 | +			  | 
|---|
 | 112 | +	}  | 
|---|
 | 113 | +  | 
|---|
 | 114 | +	public void setData(Bag<String> data)  | 
|---|
 | 115 | +	{  | 
|---|
 | 116 | +		dataList.removeAll();  | 
|---|
 | 117 | +		java.util.List<String> items=data.list();  | 
|---|
 | 118 | +		String[] s=(String[])items.toArray(new String[0]);  | 
|---|
 | 119 | +		for (int i=0; i<s.length; i++)  | 
|---|
 | 120 | +		{  | 
|---|
 | 121 | +			int num=data.num(s[i]);  | 
|---|
 | 122 | +			if (s[i]==null || s[i].length()==0)  | 
|---|
 | 123 | +				s[i]="(missing)";  | 
|---|
 | 124 | +			s[i]+=" - "+num;  | 
|---|
 | 125 | +		}  | 
|---|
 | 126 | +		dataList.setListData(s);  | 
|---|
 | 127 | +	}  | 
|---|
 | 128 | +	  | 
|---|
 | 129 | +	public Dimension getPreferredSize()  | 
|---|
 | 130 | +	{  | 
|---|
 | 131 | +		return new Dimension(200,200);  | 
|---|
 | 132 | +	}  | 
|---|
 | 133 | +  | 
|---|
 | 134 | +	@Override  | 
|---|
 | 135 | +	public ActFilter defineFilter() {  | 
|---|
 | 136 | +		Object[] o=dataList.getSelectedValues();	  | 
|---|
 | 137 | +		if (o==null || o.length==0)  | 
|---|
 | 138 | +			return null;  | 
|---|
 | 139 | +		  | 
|---|
 | 140 | +		int n=o.length;  | 
|---|
 | 141 | +		String[] s=new String[n];  | 
|---|
 | 142 | +		for (int i=0; i<n; i++)  | 
|---|
 | 143 | +		{  | 
|---|
 | 144 | +			String w=(String)o[i];  | 
|---|
 | 145 | +			int m=w.lastIndexOf('-');  | 
|---|
 | 146 | +			s[i]=w.substring(0, m-1);  | 
|---|
 | 147 | +			if ("(missing)".equals(s[i]))  | 
|---|
 | 148 | +				s[i]="";  | 
|---|
 | 149 | +		}  | 
|---|
 | 150 | +		  | 
|---|
 | 151 | +		if (s.length==1)  | 
|---|
 | 152 | +			return new FieldValueFilter(field, s[0]);  | 
|---|
 | 153 | +		FieldValueSetFilter f=new FieldValueSetFilter(field);  | 
|---|
 | 154 | +		for (int i=0; i<s.length; i++)  | 
|---|
 | 155 | +			f.addValue(s[i]);  | 
|---|
 | 156 | +		return f;  | 
|---|
 | 157 | +	}  | 
|---|
 | 158 | +  | 
|---|
 | 159 | +	@Override  | 
|---|
 | 160 | +	public void clearFilter() {  | 
|---|
 | 161 | +		dataList.clearSelection();  | 
|---|
 | 162 | +	}  | 
|---|
 | 163 | +	  | 
|---|
 | 164 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.ui.filter;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.model.*;  | 
|---|
 | 4 | +import timeflow.app.ui.StatusPanel;  | 
|---|
 | 5 | +import timeflow.data.db.*;  | 
|---|
 | 6 | +import timeflow.data.db.filter.*;  | 
|---|
 | 7 | +import timeflow.data.time.RoughTime;  | 
|---|
 | 8 | +  | 
|---|
 | 9 | +import timeflow.util.*;  | 
|---|
 | 10 | +  | 
|---|
 | 11 | +import java.util.*;  | 
|---|
 | 12 | +import javax.swing.*;  | 
|---|
 | 13 | +import javax.swing.event.ListSelectionEvent;  | 
|---|
 | 14 | +import javax.swing.event.ListSelectionListener;  | 
|---|
 | 15 | +  | 
|---|
 | 16 | +import java.awt.*;  | 
|---|
 | 17 | +  | 
|---|
 | 18 | +public class FilterControlPanel extends ModelPanel  | 
|---|
 | 19 | +{  | 
|---|
 | 20 | +	FacetSubpanel inside=new FacetSubpanel();  | 
|---|
 | 21 | +	SearchPanel searchPanel;  | 
|---|
 | 22 | +	boolean inverted=false;  | 
|---|
 | 23 | +	JMenu menuToSyncWith;  | 
|---|
 | 24 | +	  | 
|---|
 | 25 | +	  | 
|---|
 | 26 | +	public FilterControlPanel(TFModel model, JMenu menuToSyncWith)  | 
|---|
 | 27 | +	{  | 
|---|
 | 28 | +		super(model);  | 
|---|
 | 29 | +		this.menuToSyncWith=menuToSyncWith;  | 
|---|
 | 30 | +		searchPanel=new SearchPanel(model, this);  | 
|---|
 | 31 | +		setLayout(new BorderLayout());  | 
|---|
 | 32 | +		setBackground(Color.white);  | 
|---|
 | 33 | +		JPanel top=new JPanel();  | 
|---|
 | 34 | +		top.setBackground(Color.white);  | 
|---|
 | 35 | +		top.setLayout(new BorderLayout());  | 
|---|
 | 36 | +		top.setBackground(Color.white);  | 
|---|
 | 37 | +		  | 
|---|
 | 38 | +		top.add(new StatusPanel(model, this), BorderLayout.NORTH);  | 
|---|
 | 39 | +		top.add(searchPanel, BorderLayout.CENTER);  | 
|---|
 | 40 | +	  | 
|---|
 | 41 | +		add(top, BorderLayout.NORTH);  | 
|---|
 | 42 | +		add(inside, BorderLayout.CENTER);  | 
|---|
 | 43 | +	}  | 
|---|
 | 44 | +	  | 
|---|
 | 45 | +	void setInverted(boolean inverted)  | 
|---|
 | 46 | +	{  | 
|---|
 | 47 | +		this.inverted=inverted;  | 
|---|
 | 48 | +		makeFilter();  | 
|---|
 | 49 | +	}  | 
|---|
 | 50 | +	  | 
|---|
 | 51 | +	void makeFilter()  | 
|---|
 | 52 | +	{  | 
|---|
 | 53 | +		AndFilter filter=new AndFilter();  | 
|---|
 | 54 | +		String s=searchPanel.entry.getText();  | 
|---|
 | 55 | +		if (s.length()>0)  | 
|---|
 | 56 | +			filter.and(new StringMatchFilter(getModel().getDB(), s, true));  | 
|---|
 | 57 | +		for (FilterDefinitionPanel f: inside.facetTable.values())  | 
|---|
 | 58 | +			filter.and(f.defineFilter());  | 
|---|
 | 59 | +		getModel().setFilter(inverted ? new NotFilter(filter) : filter, this);  | 
|---|
 | 60 | +	}  | 
|---|
 | 61 | +	  | 
|---|
 | 62 | +	public void clearFilters()  | 
|---|
 | 63 | +	{  | 
|---|
 | 64 | +  | 
|---|
 | 65 | +		searchPanel.entry.setText("");  | 
|---|
 | 66 | +		for (FilterDefinitionPanel d: inside.facetTable.values())  | 
|---|
 | 67 | +			d.clearFilter();  | 
|---|
 | 68 | +		inverted=false;  | 
|---|
 | 69 | +		searchPanel.invert.setSelected(false);  | 
|---|
 | 70 | +		for (Field f:getModel().getDB().getFields())  | 
|---|
 | 71 | +		{  | 
|---|
 | 72 | +			inside.setFacet(f, false);  | 
|---|
 | 73 | +		}  | 
|---|
 | 74 | +		makeFilter();  | 
|---|
 | 75 | +	}  | 
|---|
 | 76 | +  | 
|---|
 | 77 | +	@Override  | 
|---|
 | 78 | +	public void note(TFEvent e) {  | 
|---|
 | 79 | +		if (e.affectsSchema())  | 
|---|
 | 80 | +		{  | 
|---|
 | 81 | +			inside.clearFacets();  | 
|---|
 | 82 | +			searchPanel.entry.setText("");  | 
|---|
 | 83 | +		}  | 
|---|
 | 84 | +	}  | 
|---|
 | 85 | +	  | 
|---|
 | 86 | +	public void setFacet(Field field, boolean on)  | 
|---|
 | 87 | +	{  | 
|---|
 | 88 | +		inside.setFacet(field, on);  | 
|---|
 | 89 | +		makeFilter();  | 
|---|
 | 90 | +	}  | 
|---|
 | 91 | +	  | 
|---|
 | 92 | +	class FacetSubpanel extends JPanel  | 
|---|
 | 93 | +	{	  | 
|---|
 | 94 | +		  | 
|---|
 | 95 | +		ArrayList<Field> facets=new ArrayList<Field>();  | 
|---|
 | 96 | +		HashMap<Field, FilterDefinitionPanel> facetTable=new HashMap<Field, FilterDefinitionPanel>();		  | 
|---|
 | 97 | +		  | 
|---|
 | 98 | +		FacetSubpanel()  | 
|---|
 | 99 | +		{  | 
|---|
 | 100 | +			setLayout(null);  | 
|---|
 | 101 | +			setBackground(Color.white);  | 
|---|
 | 102 | +		}  | 
|---|
 | 103 | +		  | 
|---|
 | 104 | +		FilterDefinitionPanel makePanel(Field field)  | 
|---|
 | 105 | +		{  | 
|---|
 | 106 | +			if (field.getType()==Double.class)  | 
|---|
 | 107 | +			{  | 
|---|
 | 108 | +				final FilterNumberPanel num=new FilterNumberPanel(field, new Runnable() {  | 
|---|
 | 109 | +					@Override  | 
|---|
 | 110 | +					public void run() {  | 
|---|
 | 111 | +						makeFilter();  | 
|---|
 | 112 | +					}}, FilterControlPanel.this);  | 
|---|
 | 113 | +				num.setData(DBUtils.getValues(getModel().getDB(), field));  | 
|---|
 | 114 | +				return num;  | 
|---|
 | 115 | +			}  | 
|---|
 | 116 | +			  | 
|---|
 | 117 | +			if (field.getType()==RoughTime.class)  | 
|---|
 | 118 | +			{  | 
|---|
 | 119 | +				final FilterDatePanel date=new FilterDatePanel(field, new Runnable() {  | 
|---|
 | 120 | +					@Override  | 
|---|
 | 121 | +					public void run() {  | 
|---|
 | 122 | +						makeFilter();  | 
|---|
 | 123 | +					}}, FilterControlPanel.this);  | 
|---|
 | 124 | +				date.setData(DBUtils.getValues(getModel().getDB(), field));  | 
|---|
 | 125 | +				return date;  | 
|---|
 | 126 | +			}  | 
|---|
 | 127 | +			  | 
|---|
 | 128 | +			final FilterCategoryPanel p= new FilterCategoryPanel(field, FilterControlPanel.this);  | 
|---|
 | 129 | +			p.dataList.addListSelectionListener(new ListSelectionListener() {				  | 
|---|
 | 130 | +				@Override  | 
|---|
 | 131 | +				public void valueChanged(ListSelectionEvent e) {  | 
|---|
 | 132 | +					makeFilter();	  | 
|---|
 | 133 | +				}  | 
|---|
 | 134 | +			});  | 
|---|
 | 135 | +			Bag<String> data=DBUtils.countValues(getModel().getDB().all(), field);  | 
|---|
 | 136 | +			p.setData(data);  | 
|---|
 | 137 | +			return p;  | 
|---|
 | 138 | +		}  | 
|---|
 | 139 | +		  | 
|---|
 | 140 | +		public void clearFacets()  | 
|---|
 | 141 | +		{  | 
|---|
 | 142 | +			removeAll();  | 
|---|
 | 143 | +			facets.clear();  | 
|---|
 | 144 | +			facetTable.clear();  | 
|---|
 | 145 | +			revalidate();  | 
|---|
 | 146 | +			repaint();  | 
|---|
 | 147 | +		}  | 
|---|
 | 148 | +		  | 
|---|
 | 149 | +		public void setFacet(Field field, boolean on)  | 
|---|
 | 150 | +		{  | 
|---|
 | 151 | +			FilterDefinitionPanel panel=facetTable.get(field);  | 
|---|
 | 152 | +			if (on == (panel!=null))  | 
|---|
 | 153 | +				return;  | 
|---|
 | 154 | +			if (on)  | 
|---|
 | 155 | +			{  | 
|---|
 | 156 | +				panel=makePanel(field);  | 
|---|
 | 157 | +				add(panel);  | 
|---|
 | 158 | +				facets.add(field);  | 
|---|
 | 159 | +				facetTable.put(field,panel);  | 
|---|
 | 160 | +			}  | 
|---|
 | 161 | +			else  | 
|---|
 | 162 | +			{  | 
|---|
 | 163 | +				remove(panel);  | 
|---|
 | 164 | +				facets.remove(field);  | 
|---|
 | 165 | +				facetTable.remove(field);  | 
|---|
 | 166 | +			}  | 
|---|
 | 167 | +			  | 
|---|
 | 168 | +			doFacetLayout();  | 
|---|
 | 169 | +			if (menuToSyncWith!=null)  | 
|---|
 | 170 | +				for (int i=0; i<menuToSyncWith.getItemCount(); i++)  | 
|---|
 | 171 | +				{  | 
|---|
 | 172 | +					JCheckBoxMenuItem item=(JCheckBoxMenuItem)menuToSyncWith.getItem(i);  | 
|---|
 | 173 | +					if (item.getText().equals(field.getName()))  | 
|---|
 | 174 | +					{  | 
|---|
 | 175 | +						item.setSelected(on);  | 
|---|
 | 176 | +					}  | 
|---|
 | 177 | +				}  | 
|---|
 | 178 | +			revalidate();  | 
|---|
 | 179 | +			repaint();  | 
|---|
 | 180 | +		}  | 
|---|
 | 181 | +		  | 
|---|
 | 182 | +		public void setBounds(int x, int y, int w, int h)  | 
|---|
 | 183 | +		{  | 
|---|
 | 184 | +			super.setBounds(x,y,w,h);  | 
|---|
 | 185 | +			doFacetLayout();  | 
|---|
 | 186 | +		}  | 
|---|
 | 187 | +		  | 
|---|
 | 188 | +		void doFacetLayout()  | 
|---|
 | 189 | +		{  | 
|---|
 | 190 | +			int w=getSize().width, h=getSize().height;  | 
|---|
 | 191 | +			int goodSize=0;  | 
|---|
 | 192 | +			for (Field f: facets)  | 
|---|
 | 193 | +			{  | 
|---|
 | 194 | +				FilterDefinitionPanel p=facetTable.get(f);  | 
|---|
 | 195 | +				goodSize+=p.getPreferredSize().height;  | 
|---|
 | 196 | +			}  | 
|---|
 | 197 | +			int top=0;  | 
|---|
 | 198 | +			for (Field f: facets)  | 
|---|
 | 199 | +			{  | 
|---|
 | 200 | +				FilterDefinitionPanel p=facetTable.get(f);  | 
|---|
 | 201 | +				int pref=p.getPreferredSize().height;  | 
|---|
 | 202 | +				int panelHeight=(goodSize<= h ? pref : (pref*h)/goodSize);  | 
|---|
 | 203 | +				p.setBounds(0,top,w,panelHeight);  | 
|---|
 | 204 | +				top+=panelHeight;  | 
|---|
 | 205 | +			}  | 
|---|
 | 206 | +		}  | 
|---|
 | 207 | +	}  | 
|---|
 | 208 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.ui.filter;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.data.db.*;  | 
|---|
 | 4 | +import timeflow.data.db.filter.*;  | 
|---|
 | 5 | +import timeflow.model.*;  | 
|---|
 | 6 | +  | 
|---|
 | 7 | +import java.awt.*;  | 
|---|
 | 8 | +import java.awt.event.ActionEvent;  | 
|---|
 | 9 | +import java.awt.event.ActionListener;  | 
|---|
 | 10 | +import java.text.*;  | 
|---|
 | 11 | +import java.util.Date;  | 
|---|
 | 12 | +  | 
|---|
 | 13 | +import javax.swing.*;  | 
|---|
 | 14 | +  | 
|---|
 | 15 | +// in theory it should be easy to refactor this to share code with  | 
|---|
 | 16 | +// NumberFilterPanel.  | 
|---|
 | 17 | +// but, i'm not sure how to do it in a way that doesn't make the code  | 
|---|
 | 18 | +// seem too complicated.  | 
|---|
 | 19 | +  | 
|---|
 | 20 | +public class FilterDatePanel  extends FilterDefinitionPanel   | 
|---|
 | 21 | +{  | 
|---|
 | 22 | +	BabyHistogram histogram;  | 
|---|
 | 23 | +	Field field;  | 
|---|
 | 24 | +	JTextField startEntry;  | 
|---|
 | 25 | +	JTextField endEntry;  | 
|---|
 | 26 | +	JCheckBox nullBox;  | 
|---|
 | 27 | +	Runnable action;  | 
|---|
 | 28 | +	SimpleDateFormat df=new SimpleDateFormat("MMM dd yyyy");  | 
|---|
 | 29 | +	  | 
|---|
 | 30 | +	public FilterDatePanel(final Field field, final Runnable action, final FilterControlPanel parent)  | 
|---|
 | 31 | +	{  | 
|---|
 | 32 | +		this.field=field;  | 
|---|
 | 33 | +		this.action=action;  | 
|---|
 | 34 | +		setLayout(new BorderLayout());  | 
|---|
 | 35 | +		setBorder(BorderFactory.createEmptyBorder(0,5,0,5));  | 
|---|
 | 36 | +		setBackground(Color.white);  | 
|---|
 | 37 | +		add(new FilterTitle(field, parent, false), BorderLayout.NORTH);  | 
|---|
 | 38 | +		  | 
|---|
 | 39 | +		Runnable fullAction=new Runnable()  | 
|---|
 | 40 | +		{  | 
|---|
 | 41 | +			public void run()  | 
|---|
 | 42 | +			{  | 
|---|
 | 43 | +				startEntry.setText(format(histogram.getLow()));  | 
|---|
 | 44 | +				endEntry.setText(format(histogram.getHigh()));  | 
|---|
 | 45 | +				action.run();  | 
|---|
 | 46 | +			}  | 
|---|
 | 47 | +		};  | 
|---|
 | 48 | +		  | 
|---|
 | 49 | +		histogram=new BabyHistogram(fullAction);  | 
|---|
 | 50 | +		  | 
|---|
 | 51 | +		add(histogram, BorderLayout.CENTER);  | 
|---|
 | 52 | +		  | 
|---|
 | 53 | +		JPanel bottomStuff=new JPanel();  | 
|---|
 | 54 | +		bottomStuff.setLayout(new GridLayout(2,1));  | 
|---|
 | 55 | +		add(bottomStuff, BorderLayout.SOUTH);  | 
|---|
 | 56 | +		  | 
|---|
 | 57 | +		JPanel lowHighPanel=new JPanel();  | 
|---|
 | 58 | +		bottomStuff.add(lowHighPanel);  | 
|---|
 | 59 | +		lowHighPanel.setBackground(Color.white);  | 
|---|
 | 60 | +		lowHighPanel.setLayout(new BorderLayout());  | 
|---|
 | 61 | +		Font small=parent.getModel().getDisplay().small();  | 
|---|
 | 62 | +		  | 
|---|
 | 63 | +		startEntry=new JTextField(7);  | 
|---|
 | 64 | +		startEntry.addActionListener(new ActionListener() {  | 
|---|
 | 65 | +			@Override  | 
|---|
 | 66 | +			public void actionPerformed(ActionEvent e) {  | 
|---|
 | 67 | +				setLowFromText();  | 
|---|
 | 68 | +				action.run();  | 
|---|
 | 69 | +			}});  | 
|---|
 | 70 | +		lowHighPanel.add(startEntry, BorderLayout.WEST);  | 
|---|
 | 71 | +		startEntry.setFont(small);  | 
|---|
 | 72 | +		  | 
|---|
 | 73 | +		JLabel rangeLabel=new JLabel("to", JLabel.CENTER);  | 
|---|
 | 74 | +		rangeLabel.setForeground(Color.gray);  | 
|---|
 | 75 | +		rangeLabel.setFont(small);  | 
|---|
 | 76 | +		lowHighPanel.add(rangeLabel, BorderLayout.CENTER);  | 
|---|
 | 77 | +		endEntry=new JTextField(7);  | 
|---|
 | 78 | +		lowHighPanel.add(endEntry, BorderLayout.EAST);  | 
|---|
 | 79 | +		endEntry.addActionListener(new ActionListener() {  | 
|---|
 | 80 | +			@Override  | 
|---|
 | 81 | +			public void actionPerformed(ActionEvent e) {  | 
|---|
 | 82 | +				setHighFromText();  | 
|---|
 | 83 | +				action.run();  | 
|---|
 | 84 | +			}});  | 
|---|
 | 85 | +		endEntry.setFont(small);  | 
|---|
 | 86 | +		  | 
|---|
 | 87 | +		nullBox=new JCheckBox("Include Missing Values");  | 
|---|
 | 88 | +		nullBox.addActionListener(new ActionListener() {  | 
|---|
 | 89 | +			@Override  | 
|---|
 | 90 | +			public void actionPerformed(ActionEvent e) {  | 
|---|
 | 91 | +				action.run();  | 
|---|
 | 92 | +			}});  | 
|---|
 | 93 | +		bottomStuff.add(nullBox);  | 
|---|
 | 94 | +		bottomStuff.setBackground(Color.white);  | 
|---|
 | 95 | +		nullBox.setBackground(Color.white);  | 
|---|
 | 96 | +		nullBox.setForeground(Color.gray);  | 
|---|
 | 97 | +		nullBox.setFont(small);  | 
|---|
 | 98 | +		  | 
|---|
 | 99 | +	}  | 
|---|
 | 100 | +	  | 
|---|
 | 101 | +	String format(double x)  | 
|---|
 | 102 | +	{  | 
|---|
 | 103 | +		Date date=new Date((long)x);  | 
|---|
 | 104 | +		return df.format(date);  | 
|---|
 | 105 | +	}  | 
|---|
 | 106 | +	  | 
|---|
 | 107 | +	void setLowFromText()  | 
|---|
 | 108 | +	{  | 
|---|
 | 109 | +		try  | 
|---|
 | 110 | +		{  | 
|---|
 | 111 | +			long low=df.parse(startEntry.getText()).getTime();  | 
|---|
 | 112 | +			long high=(long)histogram.getHigh();  | 
|---|
 | 113 | +			if (low>high)  | 
|---|
 | 114 | +			{  | 
|---|
 | 115 | +				high=low;  | 
|---|
 | 116 | +				endEntry.setText(startEntry.getText());  | 
|---|
 | 117 | +			}  | 
|---|
 | 118 | +			histogram.setTrueRange(low,high);  | 
|---|
 | 119 | +			  | 
|---|
 | 120 | +		}  | 
|---|
 | 121 | +		catch (Exception e)  | 
|---|
 | 122 | +		{  | 
|---|
 | 123 | +			  | 
|---|
 | 124 | +		}  | 
|---|
 | 125 | +	}  | 
|---|
 | 126 | +	  | 
|---|
 | 127 | +	  | 
|---|
 | 128 | +	void setHighFromText()  | 
|---|
 | 129 | +	{  | 
|---|
 | 130 | +		try  | 
|---|
 | 131 | +		{  | 
|---|
 | 132 | +			long high=df.parse(endEntry.getText()).getTime();  | 
|---|
 | 133 | +			double low=(long)histogram.getLow();  | 
|---|
 | 134 | +			if (low>high)  | 
|---|
 | 135 | +			{  | 
|---|
 | 136 | +				low=high;  | 
|---|
 | 137 | +				startEntry.setText(endEntry.getText());  | 
|---|
 | 138 | +			}  | 
|---|
 | 139 | +			histogram.setTrueRange(low,high);  | 
|---|
 | 140 | +			  | 
|---|
 | 141 | +		}  | 
|---|
 | 142 | +		catch (Exception e)  | 
|---|
 | 143 | +		{  | 
|---|
 | 144 | +			  | 
|---|
 | 145 | +		}		  | 
|---|
 | 146 | +	}  | 
|---|
 | 147 | +  | 
|---|
 | 148 | +	public void setData(double[] data)  | 
|---|
 | 149 | +	{  | 
|---|
 | 150 | +		histogram.setData(data);  | 
|---|
 | 151 | +		startEntry.setText(format(histogram.getLow()));  | 
|---|
 | 152 | +		endEntry.setText(format(histogram.getHigh()));  | 
|---|
 | 153 | +		repaint();  | 
|---|
 | 154 | +	}  | 
|---|
 | 155 | +	  | 
|---|
 | 156 | +	public Dimension getPreferredSize()  | 
|---|
 | 157 | +	{  | 
|---|
 | 158 | +		return new Dimension(200,160);  | 
|---|
 | 159 | +	}  | 
|---|
 | 160 | +  | 
|---|
 | 161 | +	@Override  | 
|---|
 | 162 | +	public ActFilter defineFilter() {  | 
|---|
 | 163 | +		long low=(long)histogram.getLow();  | 
|---|
 | 164 | +		long high=(long)histogram.getHigh();  | 
|---|
 | 165 | +		boolean acceptNull=nullBox.isSelected();  | 
|---|
 | 166 | +		return new TimeIntervalFilter(low, high, acceptNull, field);  | 
|---|
 | 167 | +	}  | 
|---|
 | 168 | +  | 
|---|
 | 169 | +	@Override  | 
|---|
 | 170 | +	public void clearFilter() {  | 
|---|
 | 171 | +		histogram.setRelRange(0, 1);  | 
|---|
 | 172 | +	}  | 
|---|
 | 173 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.ui.filter;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.data.db.filter.ActFilter;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +import javax.swing.*;  | 
|---|
 | 6 | +  | 
|---|
 | 7 | +public abstract class FilterDefinitionPanel extends JPanel {  | 
|---|
 | 8 | +	public abstract ActFilter defineFilter();  | 
|---|
 | 9 | +	public abstract void clearFilter();  | 
|---|
 | 10 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.ui.filter;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.data.db.*;  | 
|---|
 | 4 | +import timeflow.data.db.filter.*;  | 
|---|
 | 5 | +import timeflow.model.*;  | 
|---|
 | 6 | +  | 
|---|
 | 7 | +import java.awt.*;  | 
|---|
 | 8 | +import java.awt.event.ActionEvent;  | 
|---|
 | 9 | +import java.awt.event.ActionListener;  | 
|---|
 | 10 | +  | 
|---|
 | 11 | +import javax.swing.*;  | 
|---|
 | 12 | +  | 
|---|
 | 13 | +import timeflow.util.*;  | 
|---|
 | 14 | +  | 
|---|
 | 15 | +public class FilterNumberPanel  extends FilterDefinitionPanel   | 
|---|
 | 16 | +{  | 
|---|
 | 17 | +	BabyHistogram histogram;  | 
|---|
 | 18 | +	Field field;  | 
|---|
 | 19 | +	JTextField lowEntry;  | 
|---|
 | 20 | +	JTextField highEntry;  | 
|---|
 | 21 | +	JCheckBox nullBox;  | 
|---|
 | 22 | +	Runnable action;  | 
|---|
 | 23 | +	  | 
|---|
 | 24 | +	public FilterNumberPanel(final Field field, final Runnable action, final FilterControlPanel parent)  | 
|---|
 | 25 | +	{  | 
|---|
 | 26 | +		this.field=field;  | 
|---|
 | 27 | +		this.action=action;  | 
|---|
 | 28 | +		setLayout(new BorderLayout());  | 
|---|
 | 29 | +		setBackground(Color.white);  | 
|---|
 | 30 | +		setBorder(BorderFactory.createEmptyBorder(0,5,0,5));  | 
|---|
 | 31 | +		  | 
|---|
 | 32 | +		  | 
|---|
 | 33 | +		setBackground(Color.white);  | 
|---|
 | 34 | +		add(new FilterTitle(field, parent, false), BorderLayout.NORTH);  | 
|---|
 | 35 | +		  | 
|---|
 | 36 | +		Runnable fullAction=new Runnable()  | 
|---|
 | 37 | +		{  | 
|---|
 | 38 | +			public void run()  | 
|---|
 | 39 | +			{  | 
|---|
 | 40 | +				lowEntry.setText(format(histogram.getLow()));  | 
|---|
 | 41 | +				highEntry.setText(format(histogram.getHigh()));  | 
|---|
 | 42 | +				action.run();  | 
|---|
 | 43 | +			}  | 
|---|
 | 44 | +		};  | 
|---|
 | 45 | +		  | 
|---|
 | 46 | +		histogram=new BabyHistogram(fullAction);  | 
|---|
 | 47 | +		  | 
|---|
 | 48 | +		add(histogram, BorderLayout.CENTER);  | 
|---|
 | 49 | +		  | 
|---|
 | 50 | +		JPanel bottomStuff=new JPanel();  | 
|---|
 | 51 | +		bottomStuff.setLayout(new GridLayout(2,1));  | 
|---|
 | 52 | +		add(bottomStuff, BorderLayout.SOUTH);  | 
|---|
 | 53 | +		bottomStuff.setBackground(Color.white);  | 
|---|
 | 54 | +		  | 
|---|
 | 55 | +		JPanel lowHighPanel=new JPanel();  | 
|---|
 | 56 | +		bottomStuff.add(lowHighPanel);  | 
|---|
 | 57 | +		lowHighPanel.setBackground(Color.white);  | 
|---|
 | 58 | +		  | 
|---|
 | 59 | +		lowHighPanel.setLayout(new BorderLayout());  | 
|---|
 | 60 | +		  | 
|---|
 | 61 | +		Font small=parent.getModel().getDisplay().small();  | 
|---|
 | 62 | +		lowEntry=new JTextField(7);  | 
|---|
 | 63 | +		lowEntry.setFont(small);  | 
|---|
 | 64 | +		lowEntry.addActionListener(new ActionListener() {  | 
|---|
 | 65 | +			@Override  | 
|---|
 | 66 | +			public void actionPerformed(ActionEvent e) {  | 
|---|
 | 67 | +				setLowFromText();  | 
|---|
 | 68 | +				action.run();  | 
|---|
 | 69 | +			}});  | 
|---|
 | 70 | +		lowHighPanel.add(lowEntry, BorderLayout.WEST);  | 
|---|
 | 71 | +		JLabel rangeLabel=new JLabel("to", JLabel.CENTER);  | 
|---|
 | 72 | +		  | 
|---|
 | 73 | +		rangeLabel.setFont(small);  | 
|---|
 | 74 | +		rangeLabel.setForeground(Color.gray);  | 
|---|
 | 75 | +		lowHighPanel.add(rangeLabel, BorderLayout.CENTER);  | 
|---|
 | 76 | +		highEntry=new JTextField(7);  | 
|---|
 | 77 | +		lowHighPanel.add(highEntry, BorderLayout.EAST);  | 
|---|
 | 78 | +		highEntry.addActionListener(new ActionListener() {  | 
|---|
 | 79 | +			@Override  | 
|---|
 | 80 | +			public void actionPerformed(ActionEvent e) {  | 
|---|
 | 81 | +				setHighFromText();  | 
|---|
 | 82 | +				action.run();  | 
|---|
 | 83 | +			}});  | 
|---|
 | 84 | +		highEntry.setFont(small);  | 
|---|
 | 85 | +		  | 
|---|
 | 86 | +		nullBox=new JCheckBox("Include Missing Values");  | 
|---|
 | 87 | +		nullBox.addActionListener(new ActionListener() {  | 
|---|
 | 88 | +			@Override  | 
|---|
 | 89 | +			public void actionPerformed(ActionEvent e) {  | 
|---|
 | 90 | +				action.run();  | 
|---|
 | 91 | +			}});  | 
|---|
 | 92 | +		bottomStuff.add(nullBox);  | 
|---|
 | 93 | +		nullBox.setBackground(Color.white);  | 
|---|
 | 94 | +		nullBox.setForeground(Color.gray);  | 
|---|
 | 95 | +		nullBox.setFont(small);  | 
|---|
 | 96 | +	}  | 
|---|
 | 97 | +	  | 
|---|
 | 98 | +	String format(double x)  | 
|---|
 | 99 | +	{  | 
|---|
 | 100 | +		if (Math.abs(x)>10)  | 
|---|
 | 101 | +			return Display.format(Math.round(x));  | 
|---|
 | 102 | +		return Display.format(x);  | 
|---|
 | 103 | +	}  | 
|---|
 | 104 | +	  | 
|---|
 | 105 | +	void setLowFromText()  | 
|---|
 | 106 | +	{  | 
|---|
 | 107 | +		try  | 
|---|
 | 108 | +		{  | 
|---|
 | 109 | +			double low=Double.parseDouble(lowEntry.getText());  | 
|---|
 | 110 | +			double high=histogram.getHigh();  | 
|---|
 | 111 | +			if (low>high)  | 
|---|
 | 112 | +			{  | 
|---|
 | 113 | +				high=low;  | 
|---|
 | 114 | +				highEntry.setText(lowEntry.getText());  | 
|---|
 | 115 | +			}  | 
|---|
 | 116 | +			histogram.setTrueRange(low,high);  | 
|---|
 | 117 | +			  | 
|---|
 | 118 | +		}  | 
|---|
 | 119 | +		catch (Exception e)  | 
|---|
 | 120 | +		{  | 
|---|
 | 121 | +			  | 
|---|
 | 122 | +		}  | 
|---|
 | 123 | +	}  | 
|---|
 | 124 | +	  | 
|---|
 | 125 | +	  | 
|---|
 | 126 | +	void setHighFromText()  | 
|---|
 | 127 | +	{  | 
|---|
 | 128 | +		try  | 
|---|
 | 129 | +		{  | 
|---|
 | 130 | +			double high=Double.parseDouble(highEntry.getText());  | 
|---|
 | 131 | +			double low=histogram.getLow();  | 
|---|
 | 132 | +			if (low>high)  | 
|---|
 | 133 | +			{  | 
|---|
 | 134 | +				low=high;  | 
|---|
 | 135 | +				lowEntry.setText(highEntry.getText());  | 
|---|
 | 136 | +			}  | 
|---|
 | 137 | +			histogram.setTrueRange(low,high);  | 
|---|
 | 138 | +			  | 
|---|
 | 139 | +		}  | 
|---|
 | 140 | +		catch (Exception e)  | 
|---|
 | 141 | +		{  | 
|---|
 | 142 | +			  | 
|---|
 | 143 | +		}		  | 
|---|
 | 144 | +	}  | 
|---|
 | 145 | +  | 
|---|
 | 146 | +	public void setData(double[] data)  | 
|---|
 | 147 | +	{  | 
|---|
 | 148 | +		histogram.setData(data);  | 
|---|
 | 149 | +		lowEntry.setText(Display.format(histogram.getLow()));  | 
|---|
 | 150 | +		highEntry.setText(Display.format(histogram.getHigh()));  | 
|---|
 | 151 | +		repaint();  | 
|---|
 | 152 | +	}  | 
|---|
 | 153 | +	  | 
|---|
 | 154 | +	public Dimension getPreferredSize()  | 
|---|
 | 155 | +	{  | 
|---|
 | 156 | +		return new Dimension(200,160);  | 
|---|
 | 157 | +	}  | 
|---|
 | 158 | +  | 
|---|
 | 159 | +	@Override  | 
|---|
 | 160 | +	public ActFilter defineFilter() {  | 
|---|
 | 161 | +		double low=histogram.getLow();  | 
|---|
 | 162 | +		double high=histogram.getHigh();  | 
|---|
 | 163 | +		boolean acceptNull=nullBox.isSelected();  | 
|---|
 | 164 | +		return new NumericRangeFilter(field, low, high, acceptNull);  | 
|---|
 | 165 | +	}  | 
|---|
 | 166 | +  | 
|---|
 | 167 | +	@Override  | 
|---|
 | 168 | +	public void clearFilter() {  | 
|---|
 | 169 | +		histogram.setRelRange(0, 1);  | 
|---|
 | 170 | +	}  | 
|---|
 | 171 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.ui.filter;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import java.awt.BorderLayout;  | 
|---|
 | 4 | +import java.awt.Color;  | 
|---|
 | 5 | +import java.awt.event.MouseAdapter;  | 
|---|
 | 6 | +import java.awt.event.MouseEvent;  | 
|---|
 | 7 | +  | 
|---|
 | 8 | +import javax.swing.*;  | 
|---|
 | 9 | +  | 
|---|
 | 10 | +import timeflow.app.ui.DottedLine;  | 
|---|
 | 11 | +import timeflow.data.db.Field;  | 
|---|
 | 12 | +import timeflow.model.ModelPanel;  | 
|---|
 | 13 | +  | 
|---|
 | 14 | +import timeflow.util.*;  | 
|---|
 | 15 | +  | 
|---|
 | 16 | +public class FilterTitle extends JPanel {  | 
|---|
 | 17 | +	public FilterTitle(final Field field, final ModelPanel parent, boolean dots)  | 
|---|
 | 18 | +	{  | 
|---|
 | 19 | +		this(field.getName(), field, parent, dots);  | 
|---|
 | 20 | +	}  | 
|---|
 | 21 | +	public FilterTitle(String title, final Field field, final ModelPanel parent, boolean dots)  | 
|---|
 | 22 | +	{  | 
|---|
 | 23 | +		JPanel top=new JPanel();  | 
|---|
 | 24 | +		top.setBackground(Color.white);  | 
|---|
 | 25 | +		top.setLayout(new BorderLayout());  | 
|---|
 | 26 | +		JLabel label=new JLabel(title);  | 
|---|
 | 27 | +		JPanel pad=new Pad(30,30);  | 
|---|
 | 28 | +		pad.setBackground(Color.white);  | 
|---|
 | 29 | +		top.add(pad, BorderLayout.NORTH);  | 
|---|
 | 30 | +		top.add(label, BorderLayout.CENTER);  | 
|---|
 | 31 | +		label.setBackground(Color.white);  | 
|---|
 | 32 | +		  | 
|---|
 | 33 | +		if (parent instanceof FilterControlPanel)  | 
|---|
 | 34 | +		{  | 
|---|
 | 35 | +			ImageIcon redX=new ImageIcon("images/red_circle.gif");  | 
|---|
 | 36 | +			JLabel close=new JLabel(redX);  | 
|---|
 | 37 | +			close.setBackground(Color.white);  | 
|---|
 | 38 | +			top.add(close, BorderLayout.EAST);  | 
|---|
 | 39 | +			close.addMouseListener(new MouseAdapter() {  | 
|---|
 | 40 | +				@Override  | 
|---|
 | 41 | +				public void mousePressed(MouseEvent e) {  | 
|---|
 | 42 | +					((FilterControlPanel)parent).setFacet(field, false);  | 
|---|
 | 43 | +				}  | 
|---|
 | 44 | +				});  | 
|---|
 | 45 | +		}  | 
|---|
 | 46 | +		setLayout(new BorderLayout());  | 
|---|
 | 47 | +		add(top, BorderLayout.CENTER);  | 
|---|
 | 48 | +		  | 
|---|
 | 49 | +		if (dots)  | 
|---|
 | 50 | +			add(new DottedLine(), BorderLayout.SOUTH);  | 
|---|
 | 51 | +	}  | 
|---|
 | 52 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.app.ui.filter;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import javax.swing.*;  | 
|---|
 | 4 | +import java.awt.event.*;  | 
|---|
 | 5 | +  | 
|---|
 | 6 | +import timeflow.data.db.*;  | 
|---|
 | 7 | +import timeflow.data.time.*;  | 
|---|
 | 8 | +import timeflow.model.*;  | 
|---|
 | 9 | +  | 
|---|
 | 10 | +import java.awt.*;  | 
|---|
 | 11 | +  | 
|---|
 | 12 | +public class SearchPanel extends ModelPanel {  | 
|---|
 | 13 | +  | 
|---|
 | 14 | +	JTextField entry;  | 
|---|
 | 15 | +    JCheckBox invert;  | 
|---|
 | 16 | +	  | 
|---|
 | 17 | +	public SearchPanel(TFModel model, final FilterControlPanel f) {  | 
|---|
 | 18 | +		super(model);  | 
|---|
 | 19 | +		setBackground(Color.white);  | 
|---|
 | 20 | +		setBorder(BorderFactory.createEmptyBorder(15, 5,0,0));  | 
|---|
 | 21 | +		setLayout(new GridLayout(1,1));  | 
|---|
 | 22 | +		JPanel top=new JPanel();  | 
|---|
 | 23 | +		top.setLayout(new BorderLayout());  | 
|---|
 | 24 | +		add(top);  | 
|---|
 | 25 | +		top.setBackground(Color.white);  | 
|---|
 | 26 | +		JLabel label=model.getDisplay().label("Search");  | 
|---|
 | 27 | +		top.add(label, BorderLayout.WEST);  | 
|---|
 | 28 | +		entry=new JTextField(8);  | 
|---|
 | 29 | +		top.add(entry, BorderLayout.CENTER);  | 
|---|
 | 30 | +		entry.addActionListener(new ActionListener() {  | 
|---|
 | 31 | +			@Override  | 
|---|
 | 32 | +			public void actionPerformed(ActionEvent e) {  | 
|---|
 | 33 | +				f.makeFilter();  | 
|---|
 | 34 | +			}});  | 
|---|
 | 35 | +  | 
|---|
 | 36 | +		invert=new JCheckBox("Invert", false);  | 
|---|
 | 37 | +		top.add(invert, BorderLayout.EAST);  | 
|---|
 | 38 | +		invert.addActionListener(new ActionListener() {  | 
|---|
 | 39 | +			@Override  | 
|---|
 | 40 | +			public void actionPerformed(ActionEvent e) {  | 
|---|
 | 41 | +				f.setInverted(invert.isSelected());  | 
|---|
 | 42 | +			}});  | 
|---|
 | 43 | +		invert.setFont(f.getModel().getDisplay().small());  | 
|---|
 | 44 | +		invert.setForeground(Color.gray);  | 
|---|
 | 45 | +		invert.setBackground(Color.white);  | 
|---|
 | 46 | +	}  | 
|---|
 | 47 | +  | 
|---|
 | 48 | +	@Override  | 
|---|
 | 49 | +	public void note(TFEvent e) {  | 
|---|
 | 50 | +	}  | 
|---|
 | 51 | +  | 
|---|
 | 52 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.data.analysis;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.data.db.*;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +public interface DBAnalysis {  | 
|---|
 | 6 | +	  | 
|---|
 | 7 | +	public enum InterestLevel {IGNORE, BORING, INTERESTING, VERY_INTERESTING};  | 
|---|
 | 8 | +	  | 
|---|
 | 9 | +	public String getName();  | 
|---|
 | 10 | +	public InterestLevel perform(ActList acts);	  | 
|---|
 | 11 | +	public String[] getResultDescription();  | 
|---|
 | 12 | +	  | 
|---|
 | 13 | +	  | 
|---|
 | 14 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.data.analysis;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.data.db.*;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +public interface FieldAnalysis {  | 
|---|
 | 6 | +		  | 
|---|
 | 7 | +	public String getName();  | 
|---|
 | 8 | +	public boolean canHandleType(Class type);  | 
|---|
 | 9 | +	public DBAnalysis.InterestLevel perform(ActList acts, Field field);	  | 
|---|
 | 10 | +	public String[] getResultDescription();  | 
|---|
 | 11 | +	  | 
|---|
 | 12 | +	  | 
|---|
 | 13 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.data.analysis;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.util.*;  | 
|---|
 | 4 | +import timeflow.data.time.*;  | 
|---|
 | 5 | +import timeflow.data.analysis.DBAnalysis.InterestLevel;  | 
|---|
 | 6 | +import timeflow.data.db.*;  | 
|---|
 | 7 | +import java.util.*;  | 
|---|
 | 8 | +  | 
|---|
 | 9 | +public class FrequencyAnalysis implements FieldAnalysis {  | 
|---|
 | 10 | +	String[] description;  | 
|---|
 | 11 | +	  | 
|---|
 | 12 | +	@Override  | 
|---|
 | 13 | +	public String getName() {  | 
|---|
 | 14 | +		return "Frequency Of Values";  | 
|---|
 | 15 | +	}  | 
|---|
 | 16 | +  | 
|---|
 | 17 | +	@Override  | 
|---|
 | 18 | +	public String[] getResultDescription() {  | 
|---|
 | 19 | +		return description;  | 
|---|
 | 20 | +	}  | 
|---|
 | 21 | +  | 
|---|
 | 22 | +	@Override  | 
|---|
 | 23 | +	public InterestLevel perform(ActList acts, Field field) {  | 
|---|
 | 24 | +		Bag<Object> bag=new Bag<Object>();  | 
|---|
 | 25 | +		if (field.getType()==String[].class)  | 
|---|
 | 26 | +		{  | 
|---|
 | 27 | +			for (Act a: acts)  | 
|---|
 | 28 | +			{  | 
|---|
 | 29 | +				String[] tags=a.getTextList(field);  | 
|---|
 | 30 | +				if (tags!=null)  | 
|---|
 | 31 | +					for (String tag:tags)  | 
|---|
 | 32 | +						bag.add(tag);  | 
|---|
 | 33 | +			}  | 
|---|
 | 34 | +		}  | 
|---|
 | 35 | +		else  | 
|---|
 | 36 | +			for (Act a: acts)  | 
|---|
 | 37 | +				bag.add(a.get(field));  | 
|---|
 | 38 | +		  | 
|---|
 | 39 | +		int numItems=acts.size();  | 
|---|
 | 40 | +		int numDistinctVals=bag.size();  | 
|---|
 | 41 | +		int numNullVals=bag.num(null)+bag.num("");  | 
|---|
 | 42 | +		  | 
|---|
 | 43 | +		if (numItems==numDistinctVals)  | 
|---|
 | 44 | +			description=new String[] {"All values are defined and unique."};  | 
|---|
 | 45 | +		else if (numItems==numDistinctVals+numNullVals-1)  | 
|---|
 | 46 | +			description=new String[] {"All defined values are unique."};  | 
|---|
 | 47 | +		else if (numDistinctVals==1)  | 
|---|
 | 48 | +			description=new String[] {"This field is always equal to "+string(bag.list().get(0))};  | 
|---|
 | 49 | +		else if (numDistinctVals<4)  | 
|---|
 | 50 | +		{  | 
|---|
 | 51 | +			List<Object> all=bag.list();  | 
|---|
 | 52 | +			description=new String[] {"This field takes only "+all.size()+" values.",  | 
|---|
 | 53 | +				"which are: "+all};  | 
|---|
 | 54 | +		}  | 
|---|
 | 55 | +		else  | 
|---|
 | 56 | +		{  | 
|---|
 | 57 | +			List<Object> all=bag.list();  | 
|---|
 | 58 | +			description=new String[] {"There are "+numDistinctVals+" distinct values.",  | 
|---|
 | 59 | +					"Most common: \""+string(all.get(0))+"\", occurring "+bag.num(all.get(0))+" times."};  | 
|---|
 | 60 | +		}  | 
|---|
 | 61 | +		return InterestLevel.INTERESTING;  | 
|---|
 | 62 | +	}  | 
|---|
 | 63 | +	  | 
|---|
 | 64 | +	private static String[] empty=new String[0];  | 
|---|
 | 65 | +	static String string(Object o)  | 
|---|
 | 66 | +	{  | 
|---|
 | 67 | +		if (o==null || "".equals(o) || empty.equals(o))  | 
|---|
 | 68 | +			return "[missing]";  | 
|---|
 | 69 | +		return o.toString();  | 
|---|
 | 70 | +	}  | 
|---|
 | 71 | +  | 
|---|
 | 72 | +	@Override  | 
|---|
 | 73 | +	public boolean canHandleType(Class type) {  | 
|---|
 | 74 | +		return type==Double.class || type==String.class || type==String[].class;  | 
|---|
 | 75 | +	}  | 
|---|
 | 76 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.data.analysis;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.data.analysis.DBAnalysis.*;  | 
|---|
 | 4 | +import timeflow.data.db.*;  | 
|---|
 | 5 | +import timeflow.data.db.filter.*;  | 
|---|
 | 6 | +  | 
|---|
 | 7 | +public class MissingValueAnalysis implements FieldAnalysis {  | 
|---|
 | 8 | +  | 
|---|
 | 9 | +	int numNull;  | 
|---|
 | 10 | +	int percent;  | 
|---|
 | 11 | +	  | 
|---|
 | 12 | +	@Override  | 
|---|
 | 13 | +	public String getName() {  | 
|---|
 | 14 | +		return "Missing/Blank Values";  | 
|---|
 | 15 | +	}  | 
|---|
 | 16 | +  | 
|---|
 | 17 | +	@Override  | 
|---|
 | 18 | +	public String[] getResultDescription() {  | 
|---|
 | 19 | +		String s;  | 
|---|
 | 20 | +		if (numNull==0)  | 
|---|
 | 21 | +			s="No missing values";  | 
|---|
 | 22 | +		else if (numNull==1)  | 
|---|
 | 23 | +			s= "One missing value";  | 
|---|
 | 24 | +		else  | 
|---|
 | 25 | +			s=numNull+" missing values:  "+percent+"%";  | 
|---|
 | 26 | +		return new String[] {s};  | 
|---|
 | 27 | +	}  | 
|---|
 | 28 | +  | 
|---|
 | 29 | +	@Override  | 
|---|
 | 30 | +	public InterestLevel perform(ActList acts, Field field) {  | 
|---|
 | 31 | +		numNull=DBUtils.count(acts, new MissingValueFilter(field));  | 
|---|
 | 32 | +		percent=(int)Math.round(100*numNull/(double)acts.size());  | 
|---|
 | 33 | +		if (numNull==0)  | 
|---|
 | 34 | +			return InterestLevel.IGNORE;  | 
|---|
 | 35 | +		if (numNull<5)  | 
|---|
 | 36 | +			return InterestLevel.VERY_INTERESTING;  | 
|---|
 | 37 | +		return InterestLevel.INTERESTING;  | 
|---|
 | 38 | +	}  | 
|---|
 | 39 | +  | 
|---|
 | 40 | +	@Override  | 
|---|
 | 41 | +	public boolean canHandleType(Class type) {  | 
|---|
 | 42 | +		return true;  | 
|---|
 | 43 | +	}  | 
|---|
 | 44 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.data.analysis;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import java.sql.Date;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +import timeflow.data.analysis.DBAnalysis.*;  | 
|---|
 | 6 | +import timeflow.data.db.*;  | 
|---|
 | 7 | +import timeflow.data.db.filter.*;  | 
|---|
 | 8 | +import timeflow.data.time.RoughTime;  | 
|---|
 | 9 | +  | 
|---|
 | 10 | +public class RangeDateAnalysis implements FieldAnalysis {  | 
|---|
 | 11 | +  | 
|---|
 | 12 | +	String[] description;  | 
|---|
 | 13 | +	  | 
|---|
 | 14 | +	@Override  | 
|---|
 | 15 | +	public String getName() {  | 
|---|
 | 16 | +		return "Date Range";  | 
|---|
 | 17 | +	}  | 
|---|
 | 18 | +  | 
|---|
 | 19 | +	@Override  | 
|---|
 | 20 | +	public String[] getResultDescription() {  | 
|---|
 | 21 | +		return description;  | 
|---|
 | 22 | +	}  | 
|---|
 | 23 | +  | 
|---|
 | 24 | +	@Override  | 
|---|
 | 25 | +	public InterestLevel perform(ActList acts, Field field) {  | 
|---|
 | 26 | +		long low=0;  | 
|---|
 | 27 | +		long high=0;  | 
|---|
 | 28 | +  | 
|---|
 | 29 | +		boolean defined=false;  | 
|---|
 | 30 | +		for (Act a: acts)  | 
|---|
 | 31 | +		{  | 
|---|
 | 32 | +			if (a.get(field)==null)  | 
|---|
 | 33 | +				continue;  | 
|---|
 | 34 | +			long x=a.getTime(field).getTime();  | 
|---|
 | 35 | +			if (defined)  | 
|---|
 | 36 | +			{  | 
|---|
 | 37 | +				low=Math.min(low,x);  | 
|---|
 | 38 | +				high=Math.max(high, x);  | 
|---|
 | 39 | +			} else  | 
|---|
 | 40 | +			{  | 
|---|
 | 41 | +				defined=true;  | 
|---|
 | 42 | +				low=x;  | 
|---|
 | 43 | +				high=low;  | 
|---|
 | 44 | +			}  | 
|---|
 | 45 | +		}  | 
|---|
 | 46 | +		if (defined)  | 
|---|
 | 47 | +			description= new String[]  | 
|---|
 | 48 | +	  		{  | 
|---|
 | 49 | +	  			"Lowest value: "+new Date(low),  | 
|---|
 | 50 | +	  			"Highest value: "+new Date(high),  | 
|---|
 | 51 | +	  		};  | 
|---|
 | 52 | +		else  | 
|---|
 | 53 | +			description=new String[] {"No values defined."};  | 
|---|
 | 54 | +		  | 
|---|
 | 55 | +		return InterestLevel.INTERESTING;  | 
|---|
 | 56 | +	}  | 
|---|
 | 57 | +  | 
|---|
 | 58 | +	@Override  | 
|---|
 | 59 | +	public boolean canHandleType(Class type) {  | 
|---|
 | 60 | +		return type==RoughTime.class;  | 
|---|
 | 61 | +	}  | 
|---|
 | 62 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.data.analysis;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.data.analysis.DBAnalysis.*;  | 
|---|
 | 4 | +import timeflow.data.db.*;  | 
|---|
 | 5 | +import timeflow.data.db.filter.*;  | 
|---|
 | 6 | +import java.text.*;  | 
|---|
 | 7 | +  | 
|---|
 | 8 | +public class RangeNumberAnalysis implements FieldAnalysis {  | 
|---|
 | 9 | +  | 
|---|
 | 10 | +	String[] description;  | 
|---|
 | 11 | +	static DecimalFormat intFormat=new DecimalFormat("###,###,###,###");  | 
|---|
 | 12 | +	static DecimalFormat df=new DecimalFormat("###,###,###,###.##");  | 
|---|
 | 13 | +	  | 
|---|
 | 14 | +	@Override  | 
|---|
 | 15 | +	public String getName() {  | 
|---|
 | 16 | +		return "Value Range";  | 
|---|
 | 17 | +	}  | 
|---|
 | 18 | +  | 
|---|
 | 19 | +	@Override  | 
|---|
 | 20 | +	public String[] getResultDescription() {  | 
|---|
 | 21 | +		return description;  | 
|---|
 | 22 | +	}  | 
|---|
 | 23 | +  | 
|---|
 | 24 | +	@Override  | 
|---|
 | 25 | +	public InterestLevel perform(ActList acts, Field field) {  | 
|---|
 | 26 | +		double low=0;  | 
|---|
 | 27 | +		double high=0;  | 
|---|
 | 28 | +		int numZero=0;  | 
|---|
 | 29 | +		double sum=0;  | 
|---|
 | 30 | +		int numDefined=0;  | 
|---|
 | 31 | +  | 
|---|
 | 32 | +		boolean defined=false;  | 
|---|
 | 33 | +		for (Act a: acts)  | 
|---|
 | 34 | +		{  | 
|---|
 | 35 | +			if (a.get(field)==null)  | 
|---|
 | 36 | +				continue;  | 
|---|
 | 37 | +  | 
|---|
 | 38 | +			double x=a.getValue(field);  | 
|---|
 | 39 | +			numDefined++;  | 
|---|
 | 40 | +			sum+=x;  | 
|---|
 | 41 | +			  | 
|---|
 | 42 | +			if (x==0)  | 
|---|
 | 43 | +				numZero++;  | 
|---|
 | 44 | +			if (defined)  | 
|---|
 | 45 | +			{  | 
|---|
 | 46 | +				low=Math.min(low,x);  | 
|---|
 | 47 | +				high=Math.max(high, x);  | 
|---|
 | 48 | +			} else  | 
|---|
 | 49 | +			{  | 
|---|
 | 50 | +				defined=true;  | 
|---|
 | 51 | +				low=x;  | 
|---|
 | 52 | +				high=low;  | 
|---|
 | 53 | +			}  | 
|---|
 | 54 | +		}  | 
|---|
 | 55 | +		if (defined)  | 
|---|
 | 56 | +			description= new String[]  | 
|---|
 | 57 | +	  		{  | 
|---|
 | 58 | +				"Average: "+df.format((sum/numDefined)),  | 
|---|
 | 59 | +	  			"Lowest: "+df.format(low),  | 
|---|
 | 60 | +	  			"Highest: "+df.format(high),  | 
|---|
 | 61 | +	  			"Number of zero values: "+df.format(numZero)  | 
|---|
 | 62 | +	  		};  | 
|---|
 | 63 | +		else  | 
|---|
 | 64 | +			description=new String[] {"No values defined."};  | 
|---|
 | 65 | +		  | 
|---|
 | 66 | +		return InterestLevel.INTERESTING;  | 
|---|
 | 67 | +	}  | 
|---|
 | 68 | +	  | 
|---|
 | 69 | +	static String format(double x)  | 
|---|
 | 70 | +	{  | 
|---|
 | 71 | +		  | 
|---|
 | 72 | +		if (Math.abs(x)>.1)  | 
|---|
 | 73 | +		{  | 
|---|
 | 74 | +			if (Math.round(x)-x<.01)  | 
|---|
 | 75 | +				return intFormat.format(x);  | 
|---|
 | 76 | +			return df.format(x);  | 
|---|
 | 77 | +		}  | 
|---|
 | 78 | +		return ""+x;  | 
|---|
 | 79 | +	}  | 
|---|
 | 80 | +  | 
|---|
 | 81 | +	@Override  | 
|---|
 | 82 | +	public boolean canHandleType(Class type) {  | 
|---|
 | 83 | +		return type==Double.class;  | 
|---|
 | 84 | +	}  | 
|---|
 | 85 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.data.db;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.data.time.*;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +import java.net.URL;  | 
|---|
 | 6 | +import java.util.*;  | 
|---|
 | 7 | +  | 
|---|
 | 8 | +public interface Act {  | 
|---|
 | 9 | +	  | 
|---|
 | 10 | +	public ActDB getDB();  | 
|---|
 | 11 | +	  | 
|---|
 | 12 | +	public Object get(Field field);  | 
|---|
 | 13 | +	public double getValue(Field field);  | 
|---|
 | 14 | +	public String getString(Field field);  | 
|---|
 | 15 | +	public String[] getTextList(Field field);  | 
|---|
 | 16 | +	public RoughTime getTime(Field field);  | 
|---|
 | 17 | +	public URL getURL(Field field);  | 
|---|
 | 18 | +	  | 
|---|
 | 19 | +	public void set(Field field, Object value);  | 
|---|
 | 20 | +	public void setText(Field field, String text);  | 
|---|
 | 21 | +	public void setTextList(Field field, String[] list);  | 
|---|
 | 22 | +	public void setValue(Field field, double value);  | 
|---|
 | 23 | +	public void setTime(Field field, RoughTime time);  | 
|---|
 | 24 | +	public void setURL(Field field, URL url);  | 
|---|
 | 25 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.data.db;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import java.util.*;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +import timeflow.data.time.RoughTime;  | 
|---|
 | 6 | +  | 
|---|
 | 7 | +public abstract class ActComparator implements Comparator<Act> {  | 
|---|
 | 8 | +	  | 
|---|
 | 9 | +	protected Field field;  | 
|---|
 | 10 | +	protected boolean ascending=true;  | 
|---|
 | 11 | +	protected String description;  | 
|---|
 | 12 | +	  | 
|---|
 | 13 | +	  | 
|---|
 | 14 | +	private ActComparator(Field field, String description)  | 
|---|
 | 15 | +	{  | 
|---|
 | 16 | +		this.field=field;  | 
|---|
 | 17 | +		this.description=description;  | 
|---|
 | 18 | +	}  | 
|---|
 | 19 | +	  | 
|---|
 | 20 | +	public String getDescription()  | 
|---|
 | 21 | +	{  | 
|---|
 | 22 | +		return description + (ascending ? "" : " (descending)");  | 
|---|
 | 23 | +	}  | 
|---|
 | 24 | +	  | 
|---|
 | 25 | +	public static ActComparator by(Field field)  | 
|---|
 | 26 | +	{  | 
|---|
 | 27 | +		Class type=field.getType();  | 
|---|
 | 28 | +		if (type==Double.class)  | 
|---|
 | 29 | +			return new NumberComparator(field);  | 
|---|
 | 30 | +		if (type==String[].class)  | 
|---|
 | 31 | +			return new ArrayComparator(field);  | 
|---|
 | 32 | +		if (type==RoughTime.class)  | 
|---|
 | 33 | +			return new TimeComparator(field);  | 
|---|
 | 34 | +		return new StringComparator(field);  | 
|---|
 | 35 | +	}  | 
|---|
 | 36 | +	  | 
|---|
 | 37 | +	static class TimeComparator extends ActComparator  | 
|---|
 | 38 | +	{  | 
|---|
 | 39 | +		  | 
|---|
 | 40 | +		TimeComparator(Field field)  | 
|---|
 | 41 | +		{  | 
|---|
 | 42 | +			super(field, "by time");  | 
|---|
 | 43 | +		}  | 
|---|
 | 44 | +  | 
|---|
 | 45 | +		@Override  | 
|---|
 | 46 | +		public int compare(Act o1, Act o2) {  | 
|---|
 | 47 | +			RoughTime a1=o1.getTime(field);  | 
|---|
 | 48 | +			RoughTime a2=o2.getTime(field);  | 
|---|
 | 49 | +			if (a1==a2)  | 
|---|
 | 50 | +				return 0;  | 
|---|
 | 51 | +			if (a1==null)  | 
|---|
 | 52 | +				return ascending ? 1 : -1;  | 
|---|
 | 53 | +			if (a2==null)  | 
|---|
 | 54 | +				return ascending ? -1 : 1;  | 
|---|
 | 55 | +			int n=a1.compareTo(a2);  | 
|---|
 | 56 | +			return ascending ? n : -n;  | 
|---|
 | 57 | +		}  | 
|---|
 | 58 | +	}  | 
|---|
 | 59 | +  | 
|---|
 | 60 | +	  | 
|---|
 | 61 | +	static class ArrayComparator extends ActComparator  | 
|---|
 | 62 | +	{  | 
|---|
 | 63 | +		  | 
|---|
 | 64 | +		ArrayComparator(Field field)  | 
|---|
 | 65 | +		{  | 
|---|
 | 66 | +			super(field, "by length of "+field.getName());  | 
|---|
 | 67 | +		}  | 
|---|
 | 68 | +  | 
|---|
 | 69 | +		@Override  | 
|---|
 | 70 | +		public int compare(Act o1, Act o2) {  | 
|---|
 | 71 | +			int n=length(o1.getTextList(field))-length(o2.getTextList(field));  | 
|---|
 | 72 | +			return ascending ? n : -n;  | 
|---|
 | 73 | +		}  | 
|---|
 | 74 | +		  | 
|---|
 | 75 | +		static int length(String[] s)  | 
|---|
 | 76 | +		{  | 
|---|
 | 77 | +			return s==null ? 0 : s.length;  | 
|---|
 | 78 | +		}  | 
|---|
 | 79 | +	}  | 
|---|
 | 80 | +	  | 
|---|
 | 81 | +	static class StringComparator extends ActComparator  | 
|---|
 | 82 | +	{  | 
|---|
 | 83 | +		  | 
|---|
 | 84 | +		StringComparator(Field field)  | 
|---|
 | 85 | +		{  | 
|---|
 | 86 | +			super(field, "by "+field.getName());  | 
|---|
 | 87 | +		}  | 
|---|
 | 88 | +  | 
|---|
 | 89 | +		@Override  | 
|---|
 | 90 | +		public int compare(Act o1, Act o2) {  | 
|---|
 | 91 | +			int n=val(o1.getString(field)).toString().compareTo(val(o2.getString(field)).toString());  | 
|---|
 | 92 | +			return ascending ? n : -n;  | 
|---|
 | 93 | +		}  | 
|---|
 | 94 | +		  | 
|---|
 | 95 | +		String val(String s)  | 
|---|
 | 96 | +		{  | 
|---|
 | 97 | +			return s==null ? "" : s;  | 
|---|
 | 98 | +		}  | 
|---|
 | 99 | +	}  | 
|---|
 | 100 | +	  | 
|---|
 | 101 | +	static class NumberComparator extends ActComparator  | 
|---|
 | 102 | +	{  | 
|---|
 | 103 | +		  | 
|---|
 | 104 | +		NumberComparator(Field field)  | 
|---|
 | 105 | +		{  | 
|---|
 | 106 | +			super(field, "by "+field.getName());  | 
|---|
 | 107 | +		}  | 
|---|
 | 108 | +  | 
|---|
 | 109 | +		@Override  | 
|---|
 | 110 | +		public int compare(Act o1, Act o2) {  | 
|---|
 | 111 | +			double x=o1.getValue(field)-o2.getValue(field);  | 
|---|
 | 112 | +			int n=x>0 ? 1 : x<0 ? -1 : 0;  | 
|---|
 | 113 | +			return ascending ? n : -n;  | 
|---|
 | 114 | +		}  | 
|---|
 | 115 | +		  | 
|---|
 | 116 | +		  | 
|---|
 | 117 | +	}  | 
|---|
 | 118 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.data.db;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import java.util.*;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +import timeflow.data.db.filter.ActFilter;  | 
|---|
 | 6 | +  | 
|---|
 | 7 | +public interface ActDB extends Iterable<Act> {  | 
|---|
 | 8 | +	  | 
|---|
 | 9 | +	public String getSource();  | 
|---|
 | 10 | +	public String getDescription();  | 
|---|
 | 11 | +	public void setSource(String source);  | 
|---|
 | 12 | +	public void setDescription(String description);  | 
|---|
 | 13 | +	  | 
|---|
 | 14 | +	public List<String> getFieldKeys();  | 
|---|
 | 15 | +	public List<Field> getFields();  | 
|---|
 | 16 | +	public List<Field> getFields(Class type);  | 
|---|
 | 17 | +	public Field addField(String name, Class type);  | 
|---|
 | 18 | +	public Field getField(String name);  | 
|---|
 | 19 | +	public void deleteField(Field field);  | 
|---|
 | 20 | +	public void setAlias(Field field, String name);  | 
|---|
 | 21 | +	public void setNewFieldOrder(List<Field> newOrder);  | 
|---|
 | 22 | +	public void renameField(Field field, String name);  | 
|---|
 | 23 | +  | 
|---|
 | 24 | +	public void delete(Act act);  | 
|---|
 | 25 | +	public Act createAct();  | 
|---|
 | 26 | +	public ActList select(ActFilter filter);  | 
|---|
 | 27 | +	public ActList all();  | 
|---|
 | 28 | +	public int size();  | 
|---|
 | 29 | +	public Act get(int i);  | 
|---|
 | 30 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.data.db;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import java.util.*;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +public class ActList extends ArrayList<Act> {  | 
|---|
 | 6 | +	  | 
|---|
 | 7 | +	private ActDB db;  | 
|---|
 | 8 | +	  | 
|---|
 | 9 | +	public ActList(ActDB db)  | 
|---|
 | 10 | +	{  | 
|---|
 | 11 | +		this.db=db;  | 
|---|
 | 12 | +	}  | 
|---|
 | 13 | +	  | 
|---|
 | 14 | +	public ActDB getDB()  | 
|---|
 | 15 | +	{  | 
|---|
 | 16 | +		return db;  | 
|---|
 | 17 | +	}  | 
|---|
 | 18 | +	  | 
|---|
 | 19 | +	public ActList copy()  | 
|---|
 | 20 | +	{  | 
|---|
 | 21 | +		return (ActList)clone();  | 
|---|
 | 22 | +	}  | 
|---|
 | 23 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.data.db;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import java.net.URL;  | 
|---|
 | 4 | +import java.util.*;  | 
|---|
 | 5 | +  | 
|---|
 | 6 | +import timeflow.data.db.filter.*;  | 
|---|
 | 7 | +import timeflow.data.time.*;  | 
|---|
 | 8 | +  | 
|---|
 | 9 | +public class ArrayDB implements ActDB  | 
|---|
 | 10 | +{  | 
|---|
 | 11 | +  | 
|---|
 | 12 | +        private Schema schema;  | 
|---|
 | 13 | +        private List<Act> data = new ArrayList<Act>();  | 
|---|
 | 14 | +        private Field[] fields;  | 
|---|
 | 15 | +        private String source = "[unknown]";  | 
|---|
 | 16 | +        private String description = "";  | 
|---|
 | 17 | +  | 
|---|
 | 18 | +        public String getDescription()  | 
|---|
 | 19 | +        {  | 
|---|
 | 20 | +                return description;  | 
|---|
 | 21 | +        }  | 
|---|
 | 22 | +  | 
|---|
 | 23 | +        public void setDescription(String description)  | 
|---|
 | 24 | +        {  | 
|---|
 | 25 | +                this.description = description;  | 
|---|
 | 26 | +        }  | 
|---|
 | 27 | +  | 
|---|
 | 28 | +        public String getSource()  | 
|---|
 | 29 | +        {  | 
|---|
 | 30 | +                return source;  | 
|---|
 | 31 | +        }  | 
|---|
 | 32 | +  | 
|---|
 | 33 | +        public void setSource(String source)  | 
|---|
 | 34 | +        {  | 
|---|
 | 35 | +                this.source = source;  | 
|---|
 | 36 | +        }  | 
|---|
 | 37 | +  | 
|---|
 | 38 | +        @Override  | 
|---|
 | 39 | +        public void setAlias(Field field, String name)  | 
|---|
 | 40 | +        {  | 
|---|
 | 41 | +                schema.addAlias(field, name);  | 
|---|
 | 42 | +        }  | 
|---|
 | 43 | +  | 
|---|
 | 44 | +        public ArrayDB(String[] fieldNames, Class[] types, String source)  | 
|---|
 | 45 | +        {  | 
|---|
 | 46 | +                this.schema = new Schema();  | 
|---|
 | 47 | +                this.source = source;  | 
|---|
 | 48 | +                int n = fieldNames.length;  | 
|---|
 | 49 | +                fields = new Field[n];  | 
|---|
 | 50 | +                for (int i = 0; i < n; i++)  | 
|---|
 | 51 | +                {  | 
|---|
 | 52 | +                        fields[i] = schema.add(fieldNames[i], types[i]);  | 
|---|
 | 53 | +                        fields[i].index = i;  | 
|---|
 | 54 | +                }  | 
|---|
 | 55 | +        }  | 
|---|
 | 56 | +  | 
|---|
 | 57 | +        public Field[] getFieldArray()  | 
|---|
 | 58 | +        {  | 
|---|
 | 59 | +                return fields;  | 
|---|
 | 60 | +        }  | 
|---|
 | 61 | +  | 
|---|
 | 62 | +        @Override  | 
|---|
 | 63 | +        public Field addField(String name, Class type)  | 
|---|
 | 64 | +        {  | 
|---|
 | 65 | +  | 
|---|
 | 66 | +                int n = fields.length;  | 
|---|
 | 67 | +  | 
|---|
 | 68 | +                // make new Field.  | 
|---|
 | 69 | +                Field field = new Field(name, type);  | 
|---|
 | 70 | +                field.index = n;  | 
|---|
 | 71 | +  | 
|---|
 | 72 | +                // make new array of fields.  | 
|---|
 | 73 | +                Field[] moreFields = new Field[n + 1];  | 
|---|
 | 74 | +                System.arraycopy(fields, 0, moreFields, 0, n);  | 
|---|
 | 75 | +                moreFields[n] = field;  | 
|---|
 | 76 | +                this.fields = moreFields;  | 
|---|
 | 77 | +  | 
|---|
 | 78 | +                // go through all the data items and expand their arrays, too.  | 
|---|
 | 79 | +                for (Act d : data)  | 
|---|
 | 80 | +                {  | 
|---|
 | 81 | +                        IndexedAct item = (IndexedAct) d;  | 
|---|
 | 82 | +                        Object[] old = item.data;  | 
|---|
 | 83 | +                        item.data = new Object[n + 1];  | 
|---|
 | 84 | +                        System.arraycopy(old, 0, item.data, 0, n);  | 
|---|
 | 85 | +                }  | 
|---|
 | 86 | +  | 
|---|
 | 87 | +                //System.out.println("Field added: "+field);  | 
|---|
 | 88 | +                schema.add(field);  | 
|---|
 | 89 | +  | 
|---|
 | 90 | +                return field;  | 
|---|
 | 91 | +        }  | 
|---|
 | 92 | +  | 
|---|
 | 93 | +        public Field getField(String name)  | 
|---|
 | 94 | +        {  | 
|---|
 | 95 | +                return schema.getField(name);  | 
|---|
 | 96 | +        }  | 
|---|
 | 97 | +  | 
|---|
 | 98 | +        @Override  | 
|---|
 | 99 | +        public ActList all()  | 
|---|
 | 100 | +        {  | 
|---|
 | 101 | +                return select(null);  | 
|---|
 | 102 | +        }  | 
|---|
 | 103 | +  | 
|---|
 | 104 | +        @Override  | 
|---|
 | 105 | +        public Act createAct()  | 
|---|
 | 106 | +        {  | 
|---|
 | 107 | +                IndexedAct act = new IndexedAct(this, fields.length);  | 
|---|
 | 108 | +                data.add(act);  | 
|---|
 | 109 | +                return act;  | 
|---|
 | 110 | +        }  | 
|---|
 | 111 | +  | 
|---|
 | 112 | +        @Override  | 
|---|
 | 113 | +        public void delete(Act act)  | 
|---|
 | 114 | +        {  | 
|---|
 | 115 | +                data.remove(act);  | 
|---|
 | 116 | +        }  | 
|---|
 | 117 | +  | 
|---|
 | 118 | +        @Override  | 
|---|
 | 119 | +        public void deleteField(Field deadField)  | 
|---|
 | 120 | +        {  | 
|---|
 | 121 | +  | 
|---|
 | 122 | +                System.out.println("Deleting: " + deadField);  | 
|---|
 | 123 | +  | 
|---|
 | 124 | +                schema.delete(deadField);  | 
|---|
 | 125 | +                int n = fields.length;  | 
|---|
 | 126 | +                int m = deadField.index;  | 
|---|
 | 127 | +  | 
|---|
 | 128 | +                // make new array of fields.  | 
|---|
 | 129 | +                Field[] fewerFields = new Field[n - 1];  | 
|---|
 | 130 | +                removeItem(fields, fewerFields, m);  | 
|---|
 | 131 | +                fields = fewerFields;  | 
|---|
 | 132 | +  | 
|---|
 | 133 | +                // go through all the data items and contract their arrays, too.  | 
|---|
 | 134 | +                for (Act d : data)  | 
|---|
 | 135 | +                {  | 
|---|
 | 136 | +                        IndexedAct item = (IndexedAct) d;  | 
|---|
 | 137 | +                        Object[] old = item.data;  | 
|---|
 | 138 | +                        item.data = new Object[n - 1];  | 
|---|
 | 139 | +                        removeItem(old, item.data, m);  | 
|---|
 | 140 | +                }  | 
|---|
 | 141 | +  | 
|---|
 | 142 | +                // change field indices  | 
|---|
 | 143 | +                for (int i = 0; i < fields.length; i++)  | 
|---|
 | 144 | +                {  | 
|---|
 | 145 | +                        System.out.println("fields[" + i + "]=" + fields[i]);  | 
|---|
 | 146 | +                        if (fields[i].index > deadField.index)  | 
|---|
 | 147 | +                        {  | 
|---|
 | 148 | +                                fields[i].index--;  | 
|---|
 | 149 | +                        }  | 
|---|
 | 150 | +                }  | 
|---|
 | 151 | +        }  | 
|---|
 | 152 | +  | 
|---|
 | 153 | +        private static void removeItem(Object[] a, Object[] b, int m)  | 
|---|
 | 154 | +        {  | 
|---|
 | 155 | +                int n = a.length;  | 
|---|
 | 156 | +                if (m > 0)  | 
|---|
 | 157 | +                {  | 
|---|
 | 158 | +                        System.arraycopy(a, 0, b, 0, m);  | 
|---|
 | 159 | +                }  | 
|---|
 | 160 | +                if (m < n - 1)  | 
|---|
 | 161 | +                {  | 
|---|
 | 162 | +                        System.arraycopy(a, m + 1, b, m, n - m - 1);  | 
|---|
 | 163 | +                }  | 
|---|
 | 164 | +        }  | 
|---|
 | 165 | +  | 
|---|
 | 166 | +        @Override  | 
|---|
 | 167 | +        public List<Field> getFields(Class type)  | 
|---|
 | 168 | +        {  | 
|---|
 | 169 | +                return schema.getFields(type);  | 
|---|
 | 170 | +        }  | 
|---|
 | 171 | +  | 
|---|
 | 172 | +        @Override  | 
|---|
 | 173 | +        public ActList select(ActFilter filter)  | 
|---|
 | 174 | +        {  | 
|---|
 | 175 | +                ActList set = new ActList(this);  | 
|---|
 | 176 | +                for (Act a : data)  | 
|---|
 | 177 | +                {  | 
|---|
 | 178 | +                        if (filter == null || filter.accept(a))  | 
|---|
 | 179 | +                        {  | 
|---|
 | 180 | +                                set.add(a);  | 
|---|
 | 181 | +                        }  | 
|---|
 | 182 | +                }  | 
|---|
 | 183 | +                return set;  | 
|---|
 | 184 | +        }  | 
|---|
 | 185 | +  | 
|---|
 | 186 | +        @Override  | 
|---|
 | 187 | +        public List<Field> getFields()  | 
|---|
 | 188 | +        {  | 
|---|
 | 189 | +                return schema.getFields();  | 
|---|
 | 190 | +        }  | 
|---|
 | 191 | +  | 
|---|
 | 192 | +        @Override  | 
|---|
 | 193 | +        public Act get(int i)  | 
|---|
 | 194 | +        {  | 
|---|
 | 195 | +                return data.get(i);  | 
|---|
 | 196 | +        }  | 
|---|
 | 197 | +  | 
|---|
 | 198 | +        @Override  | 
|---|
 | 199 | +        public int size()  | 
|---|
 | 200 | +        {  | 
|---|
 | 201 | +                return data.size();  | 
|---|
 | 202 | +        }  | 
|---|
 | 203 | +  | 
|---|
 | 204 | +        @Override  | 
|---|
 | 205 | +        public Iterator<Act> iterator()  | 
|---|
 | 206 | +        {  | 
|---|
 | 207 | +                return data.iterator();  | 
|---|
 | 208 | +        }  | 
|---|
 | 209 | +  | 
|---|
 | 210 | +        @Override  | 
|---|
 | 211 | +        public List<String> getFieldKeys()  | 
|---|
 | 212 | +        {  | 
|---|
 | 213 | +                return schema.getKeys();  | 
|---|
 | 214 | +        }  | 
|---|
 | 215 | +  | 
|---|
 | 216 | +        @Override  | 
|---|
 | 217 | +        public void setNewFieldOrder(List<Field> newOrder)  | 
|---|
 | 218 | +        {  | 
|---|
 | 219 | +                schema.setNewFieldOrder(newOrder);  | 
|---|
 | 220 | +        }  | 
|---|
 | 221 | +  | 
|---|
 | 222 | +        class IndexedAct implements Act  | 
|---|
 | 223 | +        {  | 
|---|
 | 224 | +  | 
|---|
 | 225 | +                Object[] data;  | 
|---|
 | 226 | +                ActDB db;  | 
|---|
 | 227 | +  | 
|---|
 | 228 | +                IndexedAct(ActDB db, int numFields)  | 
|---|
 | 229 | +                {  | 
|---|
 | 230 | +                        this.db = db;  | 
|---|
 | 231 | +                        data = new Object[numFields];  | 
|---|
 | 232 | +                }  | 
|---|
 | 233 | +  | 
|---|
 | 234 | +                @Override  | 
|---|
 | 235 | +                public String getString(Field field)  | 
|---|
 | 236 | +                {  | 
|---|
 | 237 | +  | 
|---|
 | 238 | +                        Object obj = data[field.index];  | 
|---|
 | 239 | +  | 
|---|
 | 240 | +                        if (obj == null)  | 
|---|
 | 241 | +                                return null;  | 
|---|
 | 242 | +  | 
|---|
 | 243 | +                        if (obj instanceof String[])  | 
|---|
 | 244 | +                        {  | 
|---|
 | 245 | +                                String[] strings = (String[]) obj;  | 
|---|
 | 246 | +                                String string = "";  | 
|---|
 | 247 | +                                for (String s : strings)  | 
|---|
 | 248 | +                                {  | 
|---|
 | 249 | +                                        string += s + ", ";  | 
|---|
 | 250 | +                                }  | 
|---|
 | 251 | +                                return string;  | 
|---|
 | 252 | +                        } else  | 
|---|
 | 253 | +                        {  | 
|---|
 | 254 | +                                return obj.toString();  | 
|---|
 | 255 | +                        }  | 
|---|
 | 256 | +                }  | 
|---|
 | 257 | +  | 
|---|
 | 258 | +                public void setText(Field field, String text)  | 
|---|
 | 259 | +                {  | 
|---|
 | 260 | +                        data[field.index] = text;  | 
|---|
 | 261 | +                }  | 
|---|
 | 262 | +  | 
|---|
 | 263 | +                @Override  | 
|---|
 | 264 | +                public String[] getTextList(Field field)  | 
|---|
 | 265 | +                {  | 
|---|
 | 266 | +                        Object obj = data[field.index];  | 
|---|
 | 267 | +                          | 
|---|
 | 268 | +                        if (obj == null)  | 
|---|
 | 269 | +                                return null;  | 
|---|
 | 270 | +  | 
|---|
 | 271 | +                        if (obj instanceof String[])  | 
|---|
 | 272 | +                        {  | 
|---|
 | 273 | +                                return (String[]) obj;  | 
|---|
 | 274 | +                        } else  | 
|---|
 | 275 | +                        {  | 
|---|
 | 276 | +                                return new String[]  | 
|---|
 | 277 | +                                        {  | 
|---|
 | 278 | +                                                obj.toString()  | 
|---|
 | 279 | +                                        };  | 
|---|
 | 280 | +                        }  | 
|---|
 | 281 | +                }  | 
|---|
 | 282 | +  | 
|---|
 | 283 | +                public void setTextList(Field field, String[] list)  | 
|---|
 | 284 | +                {  | 
|---|
 | 285 | +                        data[field.index] = list;  | 
|---|
 | 286 | +                }  | 
|---|
 | 287 | +  | 
|---|
 | 288 | +                @Override  | 
|---|
 | 289 | +                public double getValue(Field field)  | 
|---|
 | 290 | +                {  | 
|---|
 | 291 | +                        Double d = (Double) data[field.index];  | 
|---|
 | 292 | +                        return d == null ? Double.NaN : d.doubleValue();  | 
|---|
 | 293 | +                }  | 
|---|
 | 294 | +  | 
|---|
 | 295 | +                public void setValue(Field field, double value)  | 
|---|
 | 296 | +                {  | 
|---|
 | 297 | +                        data[field.index] = value;  | 
|---|
 | 298 | +                }  | 
|---|
 | 299 | +  | 
|---|
 | 300 | +                @Override  | 
|---|
 | 301 | +                public Object get(Field field)  | 
|---|
 | 302 | +                {  | 
|---|
 | 303 | +                        return data[field.index];  | 
|---|
 | 304 | +                }  | 
|---|
 | 305 | +  | 
|---|
 | 306 | +                @Override  | 
|---|
 | 307 | +                public ActDB getDB()  | 
|---|
 | 308 | +                {  | 
|---|
 | 309 | +                        return db;  | 
|---|
 | 310 | +                }  | 
|---|
 | 311 | +  | 
|---|
 | 312 | +                @Override  | 
|---|
 | 313 | +                public void set(Field field, Object value)  | 
|---|
 | 314 | +                {  | 
|---|
 | 315 | +                        data[field.index] = value;  | 
|---|
 | 316 | +                }  | 
|---|
 | 317 | +  | 
|---|
 | 318 | +                @Override  | 
|---|
 | 319 | +                public RoughTime getTime(Field field)  | 
|---|
 | 320 | +                {  | 
|---|
 | 321 | +                        return (RoughTime) data[field.index];  | 
|---|
 | 322 | +                }  | 
|---|
 | 323 | +  | 
|---|
 | 324 | +                @Override  | 
|---|
 | 325 | +                public void setTime(Field field, RoughTime time)  | 
|---|
 | 326 | +                {  | 
|---|
 | 327 | +                        data[field.index] = time;  | 
|---|
 | 328 | +                }  | 
|---|
 | 329 | +  | 
|---|
 | 330 | +                @Override  | 
|---|
 | 331 | +                public URL getURL(Field field)  | 
|---|
 | 332 | +                {  | 
|---|
 | 333 | +                        return (URL) data[field.index];  | 
|---|
 | 334 | +                }  | 
|---|
 | 335 | +  | 
|---|
 | 336 | +                @Override  | 
|---|
 | 337 | +                public void setURL(Field field, URL url)  | 
|---|
 | 338 | +                {  | 
|---|
 | 339 | +                        data[field.index] = url;  | 
|---|
 | 340 | +                }  | 
|---|
 | 341 | +        }  | 
|---|
 | 342 | +  | 
|---|
 | 343 | +        @Override  | 
|---|
 | 344 | +        public void renameField(Field field, String name)  | 
|---|
 | 345 | +        {  | 
|---|
 | 346 | +                schema.renameField(field, name);  | 
|---|
 | 347 | +        }  | 
|---|
 | 348 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.data.db;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.data.db.*;  | 
|---|
 | 4 | +import timeflow.data.time.*;  | 
|---|
 | 5 | +  | 
|---|
 | 6 | +import java.net.URL;  | 
|---|
 | 7 | +import java.util.*;  | 
|---|
 | 8 | +  | 
|---|
 | 9 | +public class BasicAct implements Act {  | 
|---|
 | 10 | +	  | 
|---|
 | 11 | +	private HashMap data=new HashMap();  | 
|---|
 | 12 | +	private ActDB db;  | 
|---|
 | 13 | +	  | 
|---|
 | 14 | +	public BasicAct(ActDB db)  | 
|---|
 | 15 | +	{  | 
|---|
 | 16 | +		this.db=db;  | 
|---|
 | 17 | +	}  | 
|---|
 | 18 | +	  | 
|---|
 | 19 | +  | 
|---|
 | 20 | +	@Override  | 
|---|
 | 21 | +	public String getString(Field field) {  | 
|---|
 | 22 | +		return (String)data.get(field.getName());  | 
|---|
 | 23 | +	}  | 
|---|
 | 24 | +	  | 
|---|
 | 25 | +	public void setText(Field field, String text)  | 
|---|
 | 26 | +	{  | 
|---|
 | 27 | +		data.put(field.getName(), text);  | 
|---|
 | 28 | +	}  | 
|---|
 | 29 | +  | 
|---|
 | 30 | +	@Override  | 
|---|
 | 31 | +	public String[] getTextList(Field field) {  | 
|---|
 | 32 | +		return (String[])data.get(field.getName());  | 
|---|
 | 33 | +	}  | 
|---|
 | 34 | +	  | 
|---|
 | 35 | +	public void setTextList(Field field, String[] list){  | 
|---|
 | 36 | +		data.put(field.getName(), list);  | 
|---|
 | 37 | +	}  | 
|---|
 | 38 | +  | 
|---|
 | 39 | +	@Override  | 
|---|
 | 40 | +	public double getValue(Field field) {  | 
|---|
 | 41 | +		return (Double)data.get(field.getName());  | 
|---|
 | 42 | +	}  | 
|---|
 | 43 | +	  | 
|---|
 | 44 | +	public void setValue(Field field, double value)  | 
|---|
 | 45 | +	{  | 
|---|
 | 46 | +		data.put(field.getName(), value);  | 
|---|
 | 47 | +	}  | 
|---|
 | 48 | +  | 
|---|
 | 49 | +	@Override  | 
|---|
 | 50 | +	public Object get(Field field) {  | 
|---|
 | 51 | +		return data.get(field.getName());  | 
|---|
 | 52 | +	}  | 
|---|
 | 53 | +  | 
|---|
 | 54 | +	@Override  | 
|---|
 | 55 | +	public ActDB getDB() {  | 
|---|
 | 56 | +		return db;  | 
|---|
 | 57 | +	}  | 
|---|
 | 58 | +  | 
|---|
 | 59 | +	@Override  | 
|---|
 | 60 | +	public void set(Field field, Object value) {  | 
|---|
 | 61 | +		data.put(field.getName(), value);  | 
|---|
 | 62 | +	}  | 
|---|
 | 63 | +  | 
|---|
 | 64 | +  | 
|---|
 | 65 | +	@Override  | 
|---|
 | 66 | +	public RoughTime getTime(Field field) {  | 
|---|
 | 67 | +		return (RoughTime)data.get(field.getName());  | 
|---|
 | 68 | +	}  | 
|---|
 | 69 | +  | 
|---|
 | 70 | +  | 
|---|
 | 71 | +	@Override  | 
|---|
 | 72 | +	public void setTime(Field field, RoughTime time) {  | 
|---|
 | 73 | +		data.put(field.getName(), time);  | 
|---|
 | 74 | +		  | 
|---|
 | 75 | +	}  | 
|---|
 | 76 | +  | 
|---|
 | 77 | +  | 
|---|
 | 78 | +	@Override  | 
|---|
 | 79 | +	public URL getURL(Field field) {  | 
|---|
 | 80 | +		return (URL)data.get(field.getName());  | 
|---|
 | 81 | +	}  | 
|---|
 | 82 | +  | 
|---|
 | 83 | +  | 
|---|
 | 84 | +	@Override  | 
|---|
 | 85 | +	public void setURL(Field field, URL url) {  | 
|---|
 | 86 | +		data.put(field.getName(), url);  | 
|---|
 | 87 | +	}  | 
|---|
 | 88 | +  | 
|---|
 | 89 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.data.db;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import java.util.*;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +import timeflow.data.db.*;  | 
|---|
 | 6 | +import timeflow.data.db.filter.ActFilter;  | 
|---|
 | 7 | +  | 
|---|
 | 8 | +public class BasicDB implements ActDB {  | 
|---|
 | 9 | +	  | 
|---|
 | 10 | +	private Schema schema;  | 
|---|
 | 11 | +	private List<Act> data=new ArrayList<Act>();  | 
|---|
 | 12 | +	private String source="[unknown]";  | 
|---|
 | 13 | +	private String description="";	  | 
|---|
 | 14 | +	public String getDescription() {  | 
|---|
 | 15 | +		return description;  | 
|---|
 | 16 | +	}  | 
|---|
 | 17 | +  | 
|---|
 | 18 | +	public void setDescription(String description) {  | 
|---|
 | 19 | +		this.description = description;  | 
|---|
 | 20 | +	}  | 
|---|
 | 21 | +	  | 
|---|
 | 22 | +	public String getSource() {  | 
|---|
 | 23 | +		return source;  | 
|---|
 | 24 | +	}  | 
|---|
 | 25 | +  | 
|---|
 | 26 | +	public void setSource(String source) {  | 
|---|
 | 27 | +		this.source = source;  | 
|---|
 | 28 | +	}  | 
|---|
 | 29 | +  | 
|---|
 | 30 | +	public BasicDB(String source)  | 
|---|
 | 31 | +	{  | 
|---|
 | 32 | +		this(new Schema(), source);  | 
|---|
 | 33 | +	}  | 
|---|
 | 34 | +	  | 
|---|
 | 35 | +	public BasicDB(Schema schema, String source)  | 
|---|
 | 36 | +	{  | 
|---|
 | 37 | +		this.schema=schema;  | 
|---|
 | 38 | +		this.source=source;  | 
|---|
 | 39 | +	}  | 
|---|
 | 40 | +	  | 
|---|
 | 41 | +	@Override  | 
|---|
 | 42 | +	public Field addField(String name, Class type) {  | 
|---|
 | 43 | +		Field field=new Field(name, type);  | 
|---|
 | 44 | +		schema.add(field);  | 
|---|
 | 45 | +		return field;  | 
|---|
 | 46 | +	}  | 
|---|
 | 47 | +	  | 
|---|
 | 48 | +	public Field getField(String name)  | 
|---|
 | 49 | +	{  | 
|---|
 | 50 | +		return schema.getField(name);  | 
|---|
 | 51 | +	}  | 
|---|
 | 52 | +  | 
|---|
 | 53 | +	@Override  | 
|---|
 | 54 | +	public ActList all() {  | 
|---|
 | 55 | +		return select(null);  | 
|---|
 | 56 | +	}  | 
|---|
 | 57 | +  | 
|---|
 | 58 | +	@Override  | 
|---|
 | 59 | +	public Act createAct() {  | 
|---|
 | 60 | +		BasicAct act=new BasicAct(this);  | 
|---|
 | 61 | +		data.add(act);  | 
|---|
 | 62 | +		return act;  | 
|---|
 | 63 | +	}  | 
|---|
 | 64 | +  | 
|---|
 | 65 | +	@Override  | 
|---|
 | 66 | +	public void delete(Act act) {  | 
|---|
 | 67 | +		data.remove(act);  | 
|---|
 | 68 | +	}  | 
|---|
 | 69 | +  | 
|---|
 | 70 | +	@Override  | 
|---|
 | 71 | +	public void deleteField(Field field) {  | 
|---|
 | 72 | +		schema.delete(field);  | 
|---|
 | 73 | +	}  | 
|---|
 | 74 | +  | 
|---|
 | 75 | +	@Override  | 
|---|
 | 76 | +	public List<Field> getFields(Class type) {  | 
|---|
 | 77 | +		return schema.getFields(type);  | 
|---|
 | 78 | +	}  | 
|---|
 | 79 | +  | 
|---|
 | 80 | +	@Override  | 
|---|
 | 81 | +	public ActList select(ActFilter filter) {  | 
|---|
 | 82 | +		ActList set=new ActList(this);  | 
|---|
 | 83 | +		for (Act a: data)  | 
|---|
 | 84 | +			if (filter==null || filter.accept(a))  | 
|---|
 | 85 | +				set.add(a);  | 
|---|
 | 86 | +		return set;  | 
|---|
 | 87 | +	}  | 
|---|
 | 88 | +  | 
|---|
 | 89 | +	@Override  | 
|---|
 | 90 | +	public List<Field> getFields() {  | 
|---|
 | 91 | +		return schema.getFields();  | 
|---|
 | 92 | +	}  | 
|---|
 | 93 | +  | 
|---|
 | 94 | +	@Override  | 
|---|
 | 95 | +	public Act get(int i) {  | 
|---|
 | 96 | +		return data.get(i);  | 
|---|
 | 97 | +	}  | 
|---|
 | 98 | +  | 
|---|
 | 99 | +	@Override  | 
|---|
 | 100 | +	public int size() {  | 
|---|
 | 101 | +		return data.size();  | 
|---|
 | 102 | +	}  | 
|---|
 | 103 | +  | 
|---|
 | 104 | +	@Override  | 
|---|
 | 105 | +	public Iterator<Act> iterator() {  | 
|---|
 | 106 | +		return data.iterator();  | 
|---|
 | 107 | +	}  | 
|---|
 | 108 | +  | 
|---|
 | 109 | +	@Override  | 
|---|
 | 110 | +	public void setAlias(Field field, String name) {  | 
|---|
 | 111 | +		schema.addAlias(field,name);  | 
|---|
 | 112 | +	}  | 
|---|
 | 113 | +  | 
|---|
 | 114 | +	@Override  | 
|---|
 | 115 | +	public List<String> getFieldKeys() {  | 
|---|
 | 116 | +		return schema.getKeys();  | 
|---|
 | 117 | +	}  | 
|---|
 | 118 | +  | 
|---|
 | 119 | +	@Override  | 
|---|
 | 120 | +	public void setNewFieldOrder(List<Field> newOrder) {  | 
|---|
 | 121 | +		schema.setNewFieldOrder(newOrder);  | 
|---|
 | 122 | +	}  | 
|---|
 | 123 | +  | 
|---|
 | 124 | +	@Override  | 
|---|
 | 125 | +	public void renameField(Field field, String name) {  | 
|---|
 | 126 | +		schema.renameField(field, name);  | 
|---|
 | 127 | +	}  | 
|---|
 | 128 | +  | 
|---|
 | 129 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.data.db;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.data.db.filter.*;  | 
|---|
 | 4 | +import timeflow.data.time.*;  | 
|---|
 | 5 | +import timeflow.util.*;  | 
|---|
 | 6 | +  | 
|---|
 | 7 | +import java.util.*;  | 
|---|
 | 8 | +  | 
|---|
 | 9 | +public class DBUtils  | 
|---|
 | 10 | +{  | 
|---|
 | 11 | +  | 
|---|
 | 12 | +        public static void dump(Act act)  | 
|---|
 | 13 | +        {  | 
|---|
 | 14 | +                List<Field> fields = act.getDB().getFields();  | 
|---|
 | 15 | +                for (Field f : fields)  | 
|---|
 | 16 | +                {  | 
|---|
 | 17 | +                        System.out.println(f.getName() + " = " + act.get(f));  | 
|---|
 | 18 | +                }  | 
|---|
 | 19 | +        }  | 
|---|
 | 20 | +  | 
|---|
 | 21 | +        public static Object get(Act act, String field)  | 
|---|
 | 22 | +        {  | 
|---|
 | 23 | +                return act.get(act.getDB().getField(field));  | 
|---|
 | 24 | +        }  | 
|---|
 | 25 | +  | 
|---|
 | 26 | +        public static List<String> getFieldAliases(ActDB db)  | 
|---|
 | 27 | +        {  | 
|---|
 | 28 | +                ArrayList<String> list = new ArrayList<String>();  | 
|---|
 | 29 | +                for (String s : db.getFieldKeys())  | 
|---|
 | 30 | +                {  | 
|---|
 | 31 | +                        if (!db.getField(s).getName().equals(s))  | 
|---|
 | 32 | +                        {  | 
|---|
 | 33 | +                                list.add(s);  | 
|---|
 | 34 | +                        }  | 
|---|
 | 35 | +                }  | 
|---|
 | 36 | +                return list;  | 
|---|
 | 37 | +        }  | 
|---|
 | 38 | +  | 
|---|
 | 39 | +        public static Interval range(ActList a, Field[] fields)  | 
|---|
 | 40 | +        {  | 
|---|
 | 41 | +                if (fields == null || fields.length == 0)  | 
|---|
 | 42 | +                {  | 
|---|
 | 43 | +                        return new Interval(0, 0);  | 
|---|
 | 44 | +                }  | 
|---|
 | 45 | +                Interval t = null;  | 
|---|
 | 46 | +                for (Act act : a)  | 
|---|
 | 47 | +                {  | 
|---|
 | 48 | +                        for (Field f : fields)  | 
|---|
 | 49 | +                        {  | 
|---|
 | 50 | +                                RoughTime d = act.getTime(f);  | 
|---|
 | 51 | +                                if (d != null && d.isDefined())  | 
|---|
 | 52 | +                                {  | 
|---|
 | 53 | +                                        if (t == null)  | 
|---|
 | 54 | +                                        {  | 
|---|
 | 55 | +                                                t = new Interval(d.getTime(), d.getTime());  | 
|---|
 | 56 | +                                        } else  | 
|---|
 | 57 | +                                        {  | 
|---|
 | 58 | +                                                t.include(d.getTime());  | 
|---|
 | 59 | +                                        }  | 
|---|
 | 60 | +                                }  | 
|---|
 | 61 | +                        }  | 
|---|
 | 62 | +                }  | 
|---|
 | 63 | +                return t != null ? t : new Interval(RoughTime.UNKNOWN, RoughTime.UNKNOWN);  | 
|---|
 | 64 | +        }  | 
|---|
 | 65 | +  | 
|---|
 | 66 | +        public static Interval range(ActList a, String fieldName)  | 
|---|
 | 67 | +        {  | 
|---|
 | 68 | +  | 
|---|
 | 69 | +                Field field = a.getDB().getField(fieldName);  | 
|---|
 | 70 | +                if (field == null || a.size() == 0)  | 
|---|
 | 71 | +                {  | 
|---|
 | 72 | +                        return new Interval(0, 0);  | 
|---|
 | 73 | +                }  | 
|---|
 | 74 | +                Interval t = null;  | 
|---|
 | 75 | +                for (Act act : a)  | 
|---|
 | 76 | +                {  | 
|---|
 | 77 | +                        RoughTime d = act.getTime(field);  | 
|---|
 | 78 | +                        if (d != null && d.isDefined())  | 
|---|
 | 79 | +                        {  | 
|---|
 | 80 | +                                if (t == null)  | 
|---|
 | 81 | +                                {  | 
|---|
 | 82 | +                                        t = new Interval(d.getTime(), d.getTime());  | 
|---|
 | 83 | +                                } else  | 
|---|
 | 84 | +                                {  | 
|---|
 | 85 | +                                        t.include(d.getTime());  | 
|---|
 | 86 | +                                }  | 
|---|
 | 87 | +                        }  | 
|---|
 | 88 | +                }  | 
|---|
 | 89 | +                return t != null ? t : new Interval(RoughTime.UNKNOWN, RoughTime.UNKNOWN);  | 
|---|
 | 90 | +        }  | 
|---|
 | 91 | +  | 
|---|
 | 92 | +        public static List<Field> categoryFields(ActDB db)  | 
|---|
 | 93 | +        {  | 
|---|
 | 94 | +                List<Field> list = new ArrayList<Field>();  | 
|---|
 | 95 | +                list.addAll(db.getFields());  | 
|---|
 | 96 | +//                list.addAll(db.getFields(String.class));  | 
|---|
 | 97 | +//                list.addAll(db.getFields(String[].class));  | 
|---|
 | 98 | +                return list;  | 
|---|
 | 99 | +        }  | 
|---|
 | 100 | +  | 
|---|
 | 101 | +        public static int count(Iterable<Act> acts, Interval i, Field field)//String fieldName)  | 
|---|
 | 102 | +        {  | 
|---|
 | 103 | +                return count(acts, new TimeIntervalFilter(i, field));  | 
|---|
 | 104 | +        }  | 
|---|
 | 105 | +  | 
|---|
 | 106 | +        public static int count(Iterable<Act> acts, ActFilter filter)  | 
|---|
 | 107 | +        {  | 
|---|
 | 108 | +                int num = 0;  | 
|---|
 | 109 | +                for (Act a : acts)  | 
|---|
 | 110 | +                {  | 
|---|
 | 111 | +                        if (filter.accept(a))  | 
|---|
 | 112 | +                        {  | 
|---|
 | 113 | +                                num++;  | 
|---|
 | 114 | +                        }  | 
|---|
 | 115 | +                }  | 
|---|
 | 116 | +                return num;  | 
|---|
 | 117 | +        }  | 
|---|
 | 118 | +  | 
|---|
 | 119 | +        public static double[] minmax(Iterable<Act> acts, Field field)  | 
|---|
 | 120 | +        {  | 
|---|
 | 121 | +                double min = Double.NaN;  | 
|---|
 | 122 | +                double max = min;  | 
|---|
 | 123 | +                for (Act a : acts)  | 
|---|
 | 124 | +                {  | 
|---|
 | 125 | +                        double x = a.getValue(field);  | 
|---|
 | 126 | +                        if (Double.isNaN(min))  | 
|---|
 | 127 | +                        {  | 
|---|
 | 128 | +                                min = x;  | 
|---|
 | 129 | +                                max = x;  | 
|---|
 | 130 | +                        } else if (!Double.isNaN(x))  | 
|---|
 | 131 | +                        {  | 
|---|
 | 132 | +                                min = Math.min(x, min);  | 
|---|
 | 133 | +                                max = Math.max(x, max);  | 
|---|
 | 134 | +                        }  | 
|---|
 | 135 | +                }  | 
|---|
 | 136 | +                return new double[]  | 
|---|
 | 137 | +                        {  | 
|---|
 | 138 | +                                min, max  | 
|---|
 | 139 | +                        };  | 
|---|
 | 140 | +        }  | 
|---|
 | 141 | +  | 
|---|
 | 142 | +        public static double[] getValues(Iterable<Act> acts, Field field)  | 
|---|
 | 143 | +        {  | 
|---|
 | 144 | +                ArrayList<Double> list = new ArrayList<Double>();  | 
|---|
 | 145 | +                if (field.getType() == Double.class)  | 
|---|
 | 146 | +                {  | 
|---|
 | 147 | +                        for (Act a : acts)  | 
|---|
 | 148 | +                        {  | 
|---|
 | 149 | +                                list.add(a.getValue(field));  | 
|---|
 | 150 | +                        }  | 
|---|
 | 151 | +                } else if (field.getType() == RoughTime.class)  | 
|---|
 | 152 | +                {  | 
|---|
 | 153 | +                        for (Act a : acts)  | 
|---|
 | 154 | +                        {  | 
|---|
 | 155 | +                                RoughTime r = a.getTime(field);  | 
|---|
 | 156 | +                                if (r != null)  | 
|---|
 | 157 | +                                {  | 
|---|
 | 158 | +                                        list.add(new Double(r.getTime()));  | 
|---|
 | 159 | +                                }  | 
|---|
 | 160 | +                        }  | 
|---|
 | 161 | +                }  | 
|---|
 | 162 | +                int n = list.size();  | 
|---|
 | 163 | +                double[] x = new double[n];  | 
|---|
 | 164 | +                for (int i = 0; i < n; i++)  | 
|---|
 | 165 | +                {  | 
|---|
 | 166 | +                        x[i] = list.get(i);  | 
|---|
 | 167 | +                }  | 
|---|
 | 168 | +                return x;  | 
|---|
 | 169 | +        }  | 
|---|
 | 170 | +  | 
|---|
 | 171 | +        public static Bag<String> countValues(Iterable<Act> acts, Field field)  | 
|---|
 | 172 | +        {  | 
|---|
 | 173 | +                Bag<String> bag = new Bag<String>();  | 
|---|
 | 174 | +                if (field.getType() != String[].class)  | 
|---|
 | 175 | +                {  | 
|---|
 | 176 | +                        for (Act a : acts)  | 
|---|
 | 177 | +                        {  | 
|---|
 | 178 | +                                bag.add(a.getString(field));  | 
|---|
 | 179 | +                        }  | 
|---|
 | 180 | +                }  | 
|---|
 | 181 | +                else // if (field.getType() == String[].class)  | 
|---|
 | 182 | +                {  | 
|---|
 | 183 | +                        for (Act a : acts)  | 
|---|
 | 184 | +                        {  | 
|---|
 | 185 | +                                String[] s = a.getTextList(field);  | 
|---|
 | 186 | +                                if (s != null)  | 
|---|
 | 187 | +                                {  | 
|---|
 | 188 | +                                        for (int i = 0; i < s.length; i++)  | 
|---|
 | 189 | +                                        {  | 
|---|
 | 190 | +                                                bag.add(s[i]);  | 
|---|
 | 191 | +                                        }  | 
|---|
 | 192 | +                                }  | 
|---|
 | 193 | +                        }  | 
|---|
 | 194 | +//                } else  | 
|---|
 | 195 | +//                {  | 
|---|
 | 196 | +//                        throw new IllegalArgumentException("Asked to count values for non-text field: " + field);  | 
|---|
 | 197 | +                }  | 
|---|
 | 198 | +                return bag;  | 
|---|
 | 199 | +        }  | 
|---|
 | 200 | +  | 
|---|
 | 201 | +        public static void setRecSizesFromCurrent(ActDB db)  | 
|---|
 | 202 | +        {  | 
|---|
 | 203 | +                // for String fields.  | 
|---|
 | 204 | +                for (Field f : db.getFields(String.class))  | 
|---|
 | 205 | +                {  | 
|---|
 | 206 | +                        int max = 0;  | 
|---|
 | 207 | +                        for (Act a : db)  | 
|---|
 | 208 | +                        {  | 
|---|
 | 209 | +                                String s = a.getString(f);  | 
|---|
 | 210 | +                                if (s != null)  | 
|---|
 | 211 | +                                {  | 
|---|
 | 212 | +                                        max = Math.max(s.length(), max);  | 
|---|
 | 213 | +                                }  | 
|---|
 | 214 | +                        }  | 
|---|
 | 215 | +                        f.setRecommendedSize(max);  | 
|---|
 | 216 | +                }  | 
|---|
 | 217 | +        }  | 
|---|
 | 218 | +  | 
|---|
 | 219 | +        public static Field ensureField(ActDB db, String name, Class type)  | 
|---|
 | 220 | +        {  | 
|---|
 | 221 | +                Field f = db.getField(name);  | 
|---|
 | 222 | +                if (f == null)  | 
|---|
 | 223 | +                {  | 
|---|
 | 224 | +                        return db.addField(name, type);  | 
|---|
 | 225 | +                } else  | 
|---|
 | 226 | +                {  | 
|---|
 | 227 | +                        if (f.getType() != type)  | 
|---|
 | 228 | +                        {  | 
|---|
 | 229 | +                                throw new IllegalArgumentException("Mismatched types: got " + type + ", expected " + f.getType());  | 
|---|
 | 230 | +                        }  | 
|---|
 | 231 | +                }  | 
|---|
 | 232 | +                return f;  | 
|---|
 | 233 | +        }  | 
|---|
 | 234 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.data.db;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +  | 
|---|
 | 4 | +public class Field {  | 
|---|
 | 5 | +	private String name;  | 
|---|
 | 6 | +	private Class type;  | 
|---|
 | 7 | +	int index;  | 
|---|
 | 8 | +	private int recommendedSize=-1;  | 
|---|
 | 9 | +	  | 
|---|
 | 10 | +	public Field(String name, Class type)  | 
|---|
 | 11 | +	{  | 
|---|
 | 12 | +		this.name=name;  | 
|---|
 | 13 | +		this.type=type;  | 
|---|
 | 14 | +	}  | 
|---|
 | 15 | +	  | 
|---|
 | 16 | +	public Field(String name, Class type, int recommendedSize)  | 
|---|
 | 17 | +	{  | 
|---|
 | 18 | +		this.name=name;  | 
|---|
 | 19 | +		this.type=type;  | 
|---|
 | 20 | +		this.recommendedSize=recommendedSize;  | 
|---|
 | 21 | +	}  | 
|---|
 | 22 | +	  | 
|---|
 | 23 | +	public int getRecommendedSize() {  | 
|---|
 | 24 | +		return recommendedSize;  | 
|---|
 | 25 | +	}  | 
|---|
 | 26 | +  | 
|---|
 | 27 | +	public void setRecommendedSize(int recommendedSize) {  | 
|---|
 | 28 | +		this.recommendedSize = recommendedSize;  | 
|---|
 | 29 | +	}  | 
|---|
 | 30 | +	  | 
|---|
 | 31 | +	void setName(String name)  | 
|---|
 | 32 | +	{  | 
|---|
 | 33 | +		this.name=name;  | 
|---|
 | 34 | +	}  | 
|---|
 | 35 | +  | 
|---|
 | 36 | +	public String getName()  | 
|---|
 | 37 | +	{  | 
|---|
 | 38 | +		return name;  | 
|---|
 | 39 | +	}  | 
|---|
 | 40 | +	  | 
|---|
 | 41 | +	public Class getType()  | 
|---|
 | 42 | +	{  | 
|---|
 | 43 | +		return type;  | 
|---|
 | 44 | +	}  | 
|---|
 | 45 | +  | 
|---|
 | 46 | +	public String toString()  | 
|---|
 | 47 | +	{  | 
|---|
 | 48 | +		return "[Field: name='"+name+"', type="+type+", index="+index+"]";  | 
|---|
 | 49 | +	}  | 
|---|
 | 50 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.data.db;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import java.util.*;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +// methods are public for testing purposes.  | 
|---|
 | 6 | +public class Schema implements Iterable<Field>  | 
|---|
 | 7 | +{  | 
|---|
 | 8 | +  | 
|---|
 | 9 | +        private Map<String, Field> schema = new HashMap<String, Field>();  | 
|---|
 | 10 | +        private List<Field> fieldList = new ArrayList<Field>(); // so we preserve field order.  | 
|---|
 | 11 | +  | 
|---|
 | 12 | +        public Iterator<Field> iterator()  | 
|---|
 | 13 | +        {  | 
|---|
 | 14 | +                return fieldList.iterator();  | 
|---|
 | 15 | +        }  | 
|---|
 | 16 | +  | 
|---|
 | 17 | +        public Field getField(String key)  | 
|---|
 | 18 | +        {  | 
|---|
 | 19 | +                return schema.get(key);  | 
|---|
 | 20 | +        }  | 
|---|
 | 21 | +  | 
|---|
 | 22 | +        public List<String> getKeys()  | 
|---|
 | 23 | +        {  | 
|---|
 | 24 | +                return new ArrayList(schema.keySet());  | 
|---|
 | 25 | +        }  | 
|---|
 | 26 | +  | 
|---|
 | 27 | +        public List<Field> getFields(Class type)  | 
|---|
 | 28 | +        {  | 
|---|
 | 29 | +                List<Field> a = new ArrayList<Field>();  | 
|---|
 | 30 | +                for (Field s : fieldList)  | 
|---|
 | 31 | +                {  | 
|---|
 | 32 | +                        if (type == null || s.getType() == type)  | 
|---|
 | 33 | +                        {  | 
|---|
 | 34 | +                                a.add(s);  | 
|---|
 | 35 | +                        }  | 
|---|
 | 36 | +                }  | 
|---|
 | 37 | +                return a;  | 
|---|
 | 38 | +        }  | 
|---|
 | 39 | +  | 
|---|
 | 40 | +        public List<Field> getFields()  | 
|---|
 | 41 | +        {  | 
|---|
 | 42 | +                return getFields(null);  | 
|---|
 | 43 | +        }  | 
|---|
 | 44 | +  | 
|---|
 | 45 | +        // not sure this actually works! removing things while iterating? to-do: test!  | 
|---|
 | 46 | +        public void delete(Field field)  | 
|---|
 | 47 | +        {  | 
|---|
 | 48 | +                if (schema.get(field.getName()) == null)  | 
|---|
 | 49 | +                {  | 
|---|
 | 50 | +                        throw new IllegalArgumentException("No field exists: " + field);  | 
|---|
 | 51 | +                }  | 
|---|
 | 52 | +  | 
|---|
 | 53 | +                Set<String> keys = new HashSet<String>(schema.keySet());  | 
|---|
 | 54 | +                for (String s : keys)  | 
|---|
 | 55 | +                {  | 
|---|
 | 56 | +                        Field f = schema.get(s);  | 
|---|
 | 57 | +                        if (f == field)  | 
|---|
 | 58 | +                        {  | 
|---|
 | 59 | +                                schema.remove(s);  | 
|---|
 | 60 | +                        }  | 
|---|
 | 61 | +                }  | 
|---|
 | 62 | +  | 
|---|
 | 63 | +                fieldList.remove(field);  | 
|---|
 | 64 | +        }  | 
|---|
 | 65 | +  | 
|---|
 | 66 | +        public void addAlias(Field field, String name)  | 
|---|
 | 67 | +        {  | 
|---|
 | 68 | +                if (field == null)  | 
|---|
 | 69 | +                {  | 
|---|
 | 70 | +                        schema.remove(name);  | 
|---|
 | 71 | +                        return;  | 
|---|
 | 72 | +                }  | 
|---|
 | 73 | +                if (!schema.values().contains(field))  | 
|---|
 | 74 | +                {  | 
|---|
 | 75 | +                        throw new IllegalArgumentException("Field does not exist in schema: " + field);  | 
|---|
 | 76 | +                }  | 
|---|
 | 77 | +                schema.put(name, field);  | 
|---|
 | 78 | +        }  | 
|---|
 | 79 | +  | 
|---|
 | 80 | +        public Field add(String name, Class type)  | 
|---|
 | 81 | +        {  | 
|---|
 | 82 | +                return add(new Field(name, type));  | 
|---|
 | 83 | +        }  | 
|---|
 | 84 | +  | 
|---|
 | 85 | +        public Field add(Field field)  | 
|---|
 | 86 | +        {  | 
|---|
 | 87 | +                if (schema.get(field.getName()) != null)  | 
|---|
 | 88 | +                {  | 
|---|
 | 89 | +                        throw new IllegalArgumentException("Schema already has field named '" + field.getName()  | 
|---|
 | 90 | +                                + "', type=" + field.getType());  | 
|---|
 | 91 | +                }  | 
|---|
 | 92 | +                schema.put(field.getName(), field);  | 
|---|
 | 93 | +                fieldList.add(field);  | 
|---|
 | 94 | +                return field;  | 
|---|
 | 95 | +        }  | 
|---|
 | 96 | +  | 
|---|
 | 97 | +        public void setNewFieldOrder(List<Field> newOrder)  | 
|---|
 | 98 | +        {  | 
|---|
 | 99 | +                // first, we go through and check that this really is a new ordering!  | 
|---|
 | 100 | +                if (newOrder.size() != fieldList.size())  | 
|---|
 | 101 | +                {  | 
|---|
 | 102 | +                        throw new IllegalArgumentException("Field lists have different sizes");  | 
|---|
 | 103 | +                }  | 
|---|
 | 104 | +                for (Field f : newOrder)  | 
|---|
 | 105 | +                {  | 
|---|
 | 106 | +                        if (!fieldList.contains(f))  | 
|---|
 | 107 | +                        {  | 
|---|
 | 108 | +                                throw new IllegalArgumentException("New field list has unexpected field: " + f);  | 
|---|
 | 109 | +                        }  | 
|---|
 | 110 | +                }  | 
|---|
 | 111 | +                fieldList = newOrder;  | 
|---|
 | 112 | +        }  | 
|---|
 | 113 | +  | 
|---|
 | 114 | +        public void print()  | 
|---|
 | 115 | +        {  | 
|---|
 | 116 | +                System.out.println(schema);  | 
|---|
 | 117 | +        }  | 
|---|
 | 118 | +  | 
|---|
 | 119 | +        public void renameField(Field field, String name)  | 
|---|
 | 120 | +        {  | 
|---|
 | 121 | +                Field old = schema.get(name);  | 
|---|
 | 122 | +                if (old != null && old != field)  | 
|---|
 | 123 | +                {  | 
|---|
 | 124 | +                        throw new IllegalArgumentException("Can't rename a field to a name that already exists: " + name);  | 
|---|
 | 125 | +                }  | 
|---|
 | 126 | +                schema.remove(field);  | 
|---|
 | 127 | +                field.setName(name);  | 
|---|
 | 128 | +                schema.put(name, field);  | 
|---|
 | 129 | +        }  | 
|---|
 | 130 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.data.db.filter;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.data.db.Act;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +public abstract class ActFilter {  | 
|---|
 | 6 | +	public abstract boolean accept(Act act);  | 
|---|
 | 7 | +	  | 
|---|
 | 8 | +	// in earlier versions we've wanted the UI to count the number of filters applied.  | 
|---|
 | 9 | +	// because of the hierarchical way filters are defined, we need this method.  | 
|---|
 | 10 | +	public int countFilters()  | 
|---|
 | 11 | +	{  | 
|---|
 | 12 | +		return 1;  | 
|---|
 | 13 | +	}  | 
|---|
 | 14 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.data.db.filter;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import java.util.*;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +import timeflow.data.db.Act;  | 
|---|
 | 6 | +  | 
|---|
 | 7 | +public class AndFilter extends ActFilter {  | 
|---|
 | 8 | +	private List<ActFilter> filters;  | 
|---|
 | 9 | +	  | 
|---|
 | 10 | +	public AndFilter()  | 
|---|
 | 11 | +	{		  | 
|---|
 | 12 | +	}  | 
|---|
 | 13 | +	  | 
|---|
 | 14 | +	public AndFilter(ActFilter a, ActFilter b)  | 
|---|
 | 15 | +	{  | 
|---|
 | 16 | +		filters=new ArrayList<ActFilter>();  | 
|---|
 | 17 | +		and(a);  | 
|---|
 | 18 | +		and(b);  | 
|---|
 | 19 | +	}  | 
|---|
 | 20 | +	  | 
|---|
 | 21 | +	public void and(ActFilter a)  | 
|---|
 | 22 | +	{  | 
|---|
 | 23 | +		if (a==null)  | 
|---|
 | 24 | +			return;  | 
|---|
 | 25 | +		if (filters==null)  | 
|---|
 | 26 | +			filters=new ArrayList<ActFilter>();  | 
|---|
 | 27 | +		filters.add(a);  | 
|---|
 | 28 | +	}  | 
|---|
 | 29 | +  | 
|---|
 | 30 | +	@Override  | 
|---|
 | 31 | +	public boolean accept(Act act) {  | 
|---|
 | 32 | +		if (filters!=null)  | 
|---|
 | 33 | +			for (ActFilter f: filters)  | 
|---|
 | 34 | +				if (!f.accept(act))  | 
|---|
 | 35 | +					return false;  | 
|---|
 | 36 | +		return true;  | 
|---|
 | 37 | +	}  | 
|---|
 | 38 | +	  | 
|---|
 | 39 | +	public int countFilters()  | 
|---|
 | 40 | +	{  | 
|---|
 | 41 | +		int sum=0;  | 
|---|
 | 42 | +		if (filters!=null)  | 
|---|
 | 43 | +			for (ActFilter f: filters)  | 
|---|
 | 44 | +				if (f!=null)  | 
|---|
 | 45 | +					sum+=f.countFilters();  | 
|---|
 | 46 | +		return sum;  | 
|---|
 | 47 | +	}  | 
|---|
 | 48 | +	  | 
|---|
 | 49 | +	  | 
|---|
 | 50 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.data.db.filter;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.data.db.Act;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +public class ConstFilter extends ActFilter {  | 
|---|
 | 6 | +	  | 
|---|
 | 7 | +	boolean result;  | 
|---|
 | 8 | +	  | 
|---|
 | 9 | +	public ConstFilter(boolean result)  | 
|---|
 | 10 | +	{  | 
|---|
 | 11 | +		this.result=result;  | 
|---|
 | 12 | +	}  | 
|---|
 | 13 | +  | 
|---|
 | 14 | +	@Override  | 
|---|
 | 15 | +	public boolean accept(Act act) {  | 
|---|
 | 16 | +		return result;  | 
|---|
 | 17 | +	}  | 
|---|
 | 18 | +	  | 
|---|
 | 19 | +	public int countFilters()  | 
|---|
 | 20 | +	{  | 
|---|
 | 21 | +		return result ? 0 : 1;  | 
|---|
 | 22 | +	}  | 
|---|
 | 23 | +  | 
|---|
 | 24 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.data.db.filter;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.data.db.*;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +public class FieldValueFilter extends ActFilter implements ValueFilter {  | 
|---|
 | 6 | +  | 
|---|
 | 7 | +	private Field field;  | 
|---|
 | 8 | +	private Object value;  | 
|---|
 | 9 | +	  | 
|---|
 | 10 | +	public FieldValueFilter(Field field, Object value)  | 
|---|
 | 11 | +	{  | 
|---|
 | 12 | +		this.field=field;  | 
|---|
 | 13 | +		this.value=value;  | 
|---|
 | 14 | +	}  | 
|---|
 | 15 | +	  | 
|---|
 | 16 | +	public boolean ok(Object o)  | 
|---|
 | 17 | +	{  | 
|---|
 | 18 | +		if (o==null)  | 
|---|
 | 19 | +			return value==null;  | 
|---|
 | 20 | +		if (o.equals(value))  | 
|---|
 | 21 | +			return true;  | 
|---|
 | 22 | +		if (o instanceof Object[])  | 
|---|
 | 23 | +		{  | 
|---|
 | 24 | +			Object[] s=(Object[] )o;  | 
|---|
 | 25 | +			for (int i=0; i<s.length; i++)  | 
|---|
 | 26 | +				if (s[i].equals(value))  | 
|---|
 | 27 | +					return true;  | 
|---|
 | 28 | +		}  | 
|---|
 | 29 | +		return false;  | 
|---|
 | 30 | +	}  | 
|---|
 | 31 | +	  | 
|---|
 | 32 | +	@Override  | 
|---|
 | 33 | +	public boolean accept(Act act) {  | 
|---|
 | 34 | +		return ok(act.get(field));	  | 
|---|
 | 35 | +	}  | 
|---|
 | 36 | +  | 
|---|
 | 37 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.data.db.filter;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.data.db.*;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +import java.util.*;  | 
|---|
 | 6 | +  | 
|---|
 | 7 | +public class FieldValueSetFilter extends ActFilter implements ValueFilter {  | 
|---|
 | 8 | +  | 
|---|
 | 9 | +	private Field field;  | 
|---|
 | 10 | +	private Set valueSet;  | 
|---|
 | 11 | +	  | 
|---|
 | 12 | +	public FieldValueSetFilter(Field field)  | 
|---|
 | 13 | +	{  | 
|---|
 | 14 | +		this.field=field;  | 
|---|
 | 15 | +		valueSet=new HashSet();  | 
|---|
 | 16 | +	}  | 
|---|
 | 17 | +	  | 
|---|
 | 18 | +	public void addValue(Object value)  | 
|---|
 | 19 | +	{  | 
|---|
 | 20 | +		valueSet.add(value);  | 
|---|
 | 21 | +	}  | 
|---|
 | 22 | +	  | 
|---|
 | 23 | +	@Override  | 
|---|
 | 24 | +	public boolean ok(Object o) {  | 
|---|
 | 25 | +		if (o instanceof Object[])  | 
|---|
 | 26 | +		{  | 
|---|
 | 27 | +			Object[] s=(Object[] )o;  | 
|---|
 | 28 | +			for (int i=0; i<s.length; i++)  | 
|---|
 | 29 | +				if (valueSet.contains(s[i]))  | 
|---|
 | 30 | +					return true;  | 
|---|
 | 31 | +		}  | 
|---|
 | 32 | +		return valueSet.contains(o);  | 
|---|
 | 33 | +	}  | 
|---|
 | 34 | +	  | 
|---|
 | 35 | +	@Override  | 
|---|
 | 36 | +	public boolean accept(Act act) {  | 
|---|
 | 37 | +		return ok(act.get(field));  | 
|---|
 | 38 | +	}  | 
|---|
 | 39 | +  | 
|---|
 | 40 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.data.db.filter;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.data.db.Act;  | 
|---|
 | 4 | +import timeflow.data.db.Field;  | 
|---|
 | 5 | +  | 
|---|
 | 6 | +public class MissingValueFilter extends ActFilter {  | 
|---|
 | 7 | +	private Field field;  | 
|---|
 | 8 | +	private boolean text, array, number;  | 
|---|
 | 9 | +	  | 
|---|
 | 10 | +	public MissingValueFilter(Field field)  | 
|---|
 | 11 | +	{  | 
|---|
 | 12 | +		this.field=field;  | 
|---|
 | 13 | +		text=field.getType()==String.class;  | 
|---|
 | 14 | +		array=field.getType()==String[].class;  | 
|---|
 | 15 | +		number=field.getType()==Double.class;  | 
|---|
 | 16 | +	}  | 
|---|
 | 17 | +  | 
|---|
 | 18 | +	@Override  | 
|---|
 | 19 | +	public boolean accept(Act act) {  | 
|---|
 | 20 | +		Object o=act.get(field);  | 
|---|
 | 21 | +		return o==null ||   | 
|---|
 | 22 | +		      number && Double.isNaN(((Double)o).doubleValue()) ||  | 
|---|
 | 23 | +		      text && "".equals(o) ||   | 
|---|
 | 24 | +		      array && ((String[])o).length==0;  | 
|---|
 | 25 | +	}  | 
|---|
 | 26 | +  | 
|---|
 | 27 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.data.db.filter;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import java.util.*;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +import timeflow.data.db.Act;  | 
|---|
 | 6 | +  | 
|---|
 | 7 | +public class NotFilter extends ActFilter {  | 
|---|
 | 8 | +	private ActFilter f;  | 
|---|
 | 9 | +	  | 
|---|
 | 10 | +	public NotFilter(ActFilter f)  | 
|---|
 | 11 | +	{  | 
|---|
 | 12 | +		this.f=f;  | 
|---|
 | 13 | +	}  | 
|---|
 | 14 | +  | 
|---|
 | 15 | +	@Override  | 
|---|
 | 16 | +	public boolean accept(Act act) {  | 
|---|
 | 17 | +		return f!=null && !f.accept(act);  | 
|---|
 | 18 | +	}  | 
|---|
 | 19 | +	  | 
|---|
 | 20 | +	public int countFilters()  | 
|---|
 | 21 | +	{  | 
|---|
 | 22 | +		return 1+f.countFilters();  | 
|---|
 | 23 | +	}  | 
|---|
 | 24 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.data.db.filter;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.data.db.*;  | 
|---|
 | 4 | +import timeflow.data.time.*;  | 
|---|
 | 5 | +  | 
|---|
 | 6 | +public class NumericRangeFilter extends ActFilter {  | 
|---|
 | 7 | +	  | 
|---|
 | 8 | +	double low, high;  | 
|---|
 | 9 | +	Field field;  | 
|---|
 | 10 | +	boolean acceptNull;  | 
|---|
 | 11 | +	  | 
|---|
 | 12 | +	public NumericRangeFilter(Field field, double low, double high, boolean acceptNull)  | 
|---|
 | 13 | +	{  | 
|---|
 | 14 | +		this.low=low;  | 
|---|
 | 15 | +		this.high=high;  | 
|---|
 | 16 | +		this.field=field;  | 
|---|
 | 17 | +		this.acceptNull=acceptNull;  | 
|---|
 | 18 | +	}  | 
|---|
 | 19 | +  | 
|---|
 | 20 | +	@Override  | 
|---|
 | 21 | +	public boolean accept(Act act) {  | 
|---|
 | 22 | +		if (field==null)  | 
|---|
 | 23 | +			return false;  | 
|---|
 | 24 | +		double x=act.getValue(field);  | 
|---|
 | 25 | +		return Double.isNaN(x) && acceptNull || x>=low && x<=high;  | 
|---|
 | 26 | +	}  | 
|---|
 | 27 | +  | 
|---|
 | 28 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.data.db.filter;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import java.util.*;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +import timeflow.data.db.Act;  | 
|---|
 | 6 | +  | 
|---|
 | 7 | +public class OrFilter extends ActFilter {  | 
|---|
 | 8 | +	private List<ActFilter> filters=new ArrayList<ActFilter>();  | 
|---|
 | 9 | +	  | 
|---|
 | 10 | +	public OrFilter(ActFilter a, ActFilter b)  | 
|---|
 | 11 | +	{  | 
|---|
 | 12 | +		or(a);  | 
|---|
 | 13 | +		or(b);  | 
|---|
 | 14 | +	}  | 
|---|
 | 15 | +	  | 
|---|
 | 16 | +	public void or(ActFilter a)  | 
|---|
 | 17 | +	{  | 
|---|
 | 18 | +		filters.add(a);  | 
|---|
 | 19 | +	}  | 
|---|
 | 20 | +  | 
|---|
 | 21 | +	@Override  | 
|---|
 | 22 | +	public boolean accept(Act act) {  | 
|---|
 | 23 | +		for (ActFilter f: filters)  | 
|---|
 | 24 | +			if (f.accept(act))  | 
|---|
 | 25 | +				return true;  | 
|---|
 | 26 | +		return false;  | 
|---|
 | 27 | +	}  | 
|---|
 | 28 | +	public int countFilters()  | 
|---|
 | 29 | +	{  | 
|---|
 | 30 | +		int sum=0;  | 
|---|
 | 31 | +		if (filters!=null)  | 
|---|
 | 32 | +			for (ActFilter f: filters)  | 
|---|
 | 33 | +				if (f!=null)  | 
|---|
 | 34 | +					sum+=f.countFilters();  | 
|---|
 | 35 | +		return sum;  | 
|---|
 | 36 | +	}  | 
|---|
 | 37 | +	  | 
|---|
 | 38 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.data.db.filter;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.data.db.*;  | 
|---|
 | 4 | +import timeflow.data.time.*;  | 
|---|
 | 5 | +  | 
|---|
 | 6 | +import java.util.regex.*;  | 
|---|
 | 7 | +  | 
|---|
 | 8 | +public class StringMatchFilter extends ActFilter {  | 
|---|
 | 9 | +  | 
|---|
 | 10 | +	private Field[] textFields;  | 
|---|
 | 11 | +	private Field[] listFields;  | 
|---|
 | 12 | +	private String query="";  | 
|---|
 | 13 | +	private boolean isRegex=false;  | 
|---|
 | 14 | +	private Pattern pattern;  | 
|---|
 | 15 | +	  | 
|---|
 | 16 | +	public StringMatchFilter(ActDB db, boolean isRegex)  | 
|---|
 | 17 | +	{  | 
|---|
 | 18 | +		this(db,"", isRegex);  | 
|---|
 | 19 | +	}  | 
|---|
 | 20 | +	  | 
|---|
 | 21 | +	public StringMatchFilter(ActDB db, String query, boolean isRegex)  | 
|---|
 | 22 | +	{  | 
|---|
 | 23 | +		textFields=(Field[])db.getFields(String.class).toArray(new Field[0]);  | 
|---|
 | 24 | +		listFields=(Field[])db.getFields(String[].class).toArray(new Field[0]);  | 
|---|
 | 25 | +		this.isRegex=isRegex;  | 
|---|
 | 26 | +		setQuery(query);  | 
|---|
 | 27 | +	}  | 
|---|
 | 28 | +	  | 
|---|
 | 29 | +	public String getQuery()  | 
|---|
 | 30 | +	{  | 
|---|
 | 31 | +		return query;  | 
|---|
 | 32 | +	}  | 
|---|
 | 33 | +	  | 
|---|
 | 34 | +	public void setQuery(String query)  | 
|---|
 | 35 | +	{  | 
|---|
 | 36 | +		this.query=query;  | 
|---|
 | 37 | +		if (isRegex)  | 
|---|
 | 38 | +		{  | 
|---|
 | 39 | +			pattern=Pattern.compile(query, Pattern.CASE_INSENSITIVE+Pattern.MULTILINE+Pattern.DOTALL);  | 
|---|
 | 40 | +		}  | 
|---|
 | 41 | +		else  | 
|---|
 | 42 | +			this.query=query.toLowerCase();  | 
|---|
 | 43 | +	}  | 
|---|
 | 44 | +	  | 
|---|
 | 45 | +	@Override  | 
|---|
 | 46 | +	public boolean accept(Act act) {  | 
|---|
 | 47 | +		// check text fields  | 
|---|
 | 48 | +		for (int i=0; i<textFields.length; i++)  | 
|---|
 | 49 | +		{  | 
|---|
 | 50 | +			String s=act.getString(textFields[i]);  | 
|---|
 | 51 | +			if (s==null) continue;  | 
|---|
 | 52 | +			if (isRegex ? pattern.matcher(s).find() : s.toLowerCase().contains(query))  | 
|---|
 | 53 | +				return true;  | 
|---|
 | 54 | +		}  | 
|---|
 | 55 | +		// check list fields  | 
|---|
 | 56 | +		for (int j=0; j<listFields.length; j++)  | 
|---|
 | 57 | +		{  | 
|---|
 | 58 | +			String[] m=act.getTextList(listFields[j]);  | 
|---|
 | 59 | +			if (m!=null)  | 
|---|
 | 60 | +				for (int i=0; i<m.length; i++)  | 
|---|
 | 61 | +				{  | 
|---|
 | 62 | +					String s=m[i];  | 
|---|
 | 63 | +					if (isRegex ? pattern.matcher(s).find() : s.toLowerCase().contains(query))  | 
|---|
 | 64 | +						return true;  | 
|---|
 | 65 | +				}  | 
|---|
 | 66 | +		}  | 
|---|
 | 67 | +		return false;  | 
|---|
 | 68 | +	}  | 
|---|
 | 69 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.data.db.filter;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.data.db.*;  | 
|---|
 | 4 | +import timeflow.data.time.*;  | 
|---|
 | 5 | +  | 
|---|
 | 6 | +public class TimeIntervalFilter extends ActFilter {  | 
|---|
 | 7 | +	  | 
|---|
 | 8 | +	Interval interval;  | 
|---|
 | 9 | +	Field timeField;  | 
|---|
 | 10 | +	boolean acceptNull;  | 
|---|
 | 11 | +	  | 
|---|
 | 12 | +	public TimeIntervalFilter(long start, long end, boolean acceptNull, Field timeField)  | 
|---|
 | 13 | +	{  | 
|---|
 | 14 | +		this.interval=new Interval(start, end);  | 
|---|
 | 15 | +		this.acceptNull=acceptNull;  | 
|---|
 | 16 | +		this.timeField=timeField;  | 
|---|
 | 17 | +	}  | 
|---|
 | 18 | +	  | 
|---|
 | 19 | +	public TimeIntervalFilter(Interval interval, Field timeField)  | 
|---|
 | 20 | +	{  | 
|---|
 | 21 | +		this.interval=interval;  | 
|---|
 | 22 | +		this.timeField=timeField;  | 
|---|
 | 23 | +	}  | 
|---|
 | 24 | +  | 
|---|
 | 25 | +	@Override  | 
|---|
 | 26 | +	public boolean accept(Act act) {  | 
|---|
 | 27 | +		if (timeField==null)  | 
|---|
 | 28 | +			return false;  | 
|---|
 | 29 | +		RoughTime t=act.getTime(timeField);  | 
|---|
 | 30 | +		if (t==null)  | 
|---|
 | 31 | +			return acceptNull;  | 
|---|
 | 32 | +		return interval.contains(t.getTime());  | 
|---|
 | 33 | +	}  | 
|---|
 | 34 | +  | 
|---|
 | 35 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.data.db.filter;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +public interface ValueFilter {  | 
|---|
 | 4 | +	public boolean ok(Object o);  | 
|---|
 | 5 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.data.time;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import java.util.*;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +public class Interval  | 
|---|
 | 6 | +{  | 
|---|
 | 7 | +  | 
|---|
 | 8 | +        public long start;  | 
|---|
 | 9 | +        public long end;  | 
|---|
 | 10 | +  | 
|---|
 | 11 | +        public Interval(long start, long end)  | 
|---|
 | 12 | +        {  | 
|---|
 | 13 | +                this.start = start;  | 
|---|
 | 14 | +                this.end = end;  | 
|---|
 | 15 | +        }  | 
|---|
 | 16 | +  | 
|---|
 | 17 | +        public Interval copy()  | 
|---|
 | 18 | +        {  | 
|---|
 | 19 | +                return new Interval(start, end);  | 
|---|
 | 20 | +        }  | 
|---|
 | 21 | +  | 
|---|
 | 22 | +        public boolean contains(long x)  | 
|---|
 | 23 | +        {  | 
|---|
 | 24 | +                return x >= start && x <= end;  | 
|---|
 | 25 | +        }  | 
|---|
 | 26 | +  | 
|---|
 | 27 | +        public boolean intersects(Interval x)  | 
|---|
 | 28 | +        {  | 
|---|
 | 29 | +                return intersects(x.start, x.end);  | 
|---|
 | 30 | +        }  | 
|---|
 | 31 | +  | 
|---|
 | 32 | +        public boolean intersects(long start1, long end1)  | 
|---|
 | 33 | +        {  | 
|---|
 | 34 | +                return start1 <= end && end1 >= start;  | 
|---|
 | 35 | +        }  | 
|---|
 | 36 | +  | 
|---|
 | 37 | +        public Interval subinterval(double startFraction, double endFraction)  | 
|---|
 | 38 | +        {  | 
|---|
 | 39 | +                return new Interval((long) (start + startFraction * length()),  | 
|---|
 | 40 | +                        (long) (start + endFraction * length()));  | 
|---|
 | 41 | +        }  | 
|---|
 | 42 | +  | 
|---|
 | 43 | +        public void setTo(long start, long end)  | 
|---|
 | 44 | +        {  | 
|---|
 | 45 | +                this.start = start;  | 
|---|
 | 46 | +                this.end = end;  | 
|---|
 | 47 | +        }  | 
|---|
 | 48 | +  | 
|---|
 | 49 | +        public void setTo(Interval t)  | 
|---|
 | 50 | +        {  | 
|---|
 | 51 | +                start = t.start;  | 
|---|
 | 52 | +                end = t.end;  | 
|---|
 | 53 | +        }  | 
|---|
 | 54 | +  | 
|---|
 | 55 | +        public void include(long time)  | 
|---|
 | 56 | +        {  | 
|---|
 | 57 | +                start = Math.min(start, time);  | 
|---|
 | 58 | +                end = Math.max(end, time);  | 
|---|
 | 59 | +        }  | 
|---|
 | 60 | +  | 
|---|
 | 61 | +        public void include(Interval t)  | 
|---|
 | 62 | +        {  | 
|---|
 | 63 | +                include(t.start);  | 
|---|
 | 64 | +                include(t.end);  | 
|---|
 | 65 | +        }  | 
|---|
 | 66 | +  | 
|---|
 | 67 | +        public void expand(long amount)  | 
|---|
 | 68 | +        {  | 
|---|
 | 69 | +                start -= amount;  | 
|---|
 | 70 | +                end += amount;  | 
|---|
 | 71 | +        }  | 
|---|
 | 72 | +  | 
|---|
 | 73 | +        public void add(long amount)  | 
|---|
 | 74 | +        {  | 
|---|
 | 75 | +                start += amount;  | 
|---|
 | 76 | +                end += amount;  | 
|---|
 | 77 | +        }  | 
|---|
 | 78 | +  | 
|---|
 | 79 | +        public long length()  | 
|---|
 | 80 | +        {  | 
|---|
 | 81 | +                return end - start;  | 
|---|
 | 82 | +        }  | 
|---|
 | 83 | +  | 
|---|
 | 84 | +        public void translateTo(long newStart)  | 
|---|
 | 85 | +        {  | 
|---|
 | 86 | +                add(newStart - start);  | 
|---|
 | 87 | +        }  | 
|---|
 | 88 | +  | 
|---|
 | 89 | +        public Interval intersection(Interval i)  | 
|---|
 | 90 | +        {  | 
|---|
 | 91 | +                start = Math.max(i.start, start);  | 
|---|
 | 92 | +                end = Math.min(i.end, end);  | 
|---|
 | 93 | +                return this;  | 
|---|
 | 94 | +        }  | 
|---|
 | 95 | +  | 
|---|
 | 96 | +        public void clampInside(Interval container)  | 
|---|
 | 97 | +        {  | 
|---|
 | 98 | +                if (length() > container.length())  | 
|---|
 | 99 | +                {  | 
|---|
 | 100 | +                        throw new IllegalArgumentException("Containing interval too small: " + container + " < " + this);  | 
|---|
 | 101 | +                }  | 
|---|
 | 102 | +                if (start >= container.start && end <= container.end)  | 
|---|
 | 103 | +                {  | 
|---|
 | 104 | +                        return;  | 
|---|
 | 105 | +                }  | 
|---|
 | 106 | +                add(Math.max(0, container.start - start));  | 
|---|
 | 107 | +                add(Math.min(0, container.end - end));  | 
|---|
 | 108 | +        }  | 
|---|
 | 109 | +  | 
|---|
 | 110 | +        public String toString()  | 
|---|
 | 111 | +        {  | 
|---|
 | 112 | +                return "[Interval: From " + new Date(start) + " to " + new Date(end) + "]";  | 
|---|
 | 113 | +        }  | 
|---|
 | 114 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.data.time;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import java.util.Calendar;  | 
|---|
 | 4 | +import java.util.Date;  | 
|---|
 | 5 | +  | 
|---|
 | 6 | +public class RoughTime implements Comparable {  | 
|---|
 | 7 | +  | 
|---|
 | 8 | +	public static final long UNKNOWN=Long.MIN_VALUE;  | 
|---|
 | 9 | +	private TimeUnit units;  | 
|---|
 | 10 | +	private long time;  | 
|---|
 | 11 | +		  | 
|---|
 | 12 | +	public RoughTime(TimeUnit units)  | 
|---|
 | 13 | +	{  | 
|---|
 | 14 | +		time=UNKNOWN;  | 
|---|
 | 15 | +		this.units=units;  | 
|---|
 | 16 | +	}  | 
|---|
 | 17 | +	  | 
|---|
 | 18 | +	public RoughTime(long time, TimeUnit units)  | 
|---|
 | 19 | +	{  | 
|---|
 | 20 | +		this.time=time;  | 
|---|
 | 21 | +		this.units=units;  | 
|---|
 | 22 | +	}  | 
|---|
 | 23 | +	  | 
|---|
 | 24 | +	public boolean isDefined()  | 
|---|
 | 25 | +	{  | 
|---|
 | 26 | +		return time!=UNKNOWN;  | 
|---|
 | 27 | +	}  | 
|---|
 | 28 | +	  | 
|---|
 | 29 | +	public long getTime()  | 
|---|
 | 30 | +	{  | 
|---|
 | 31 | +		return time;  | 
|---|
 | 32 | +	}  | 
|---|
 | 33 | +	  | 
|---|
 | 34 | +	public void setTime(long time)  | 
|---|
 | 35 | +	{  | 
|---|
 | 36 | +		this.time=time;  | 
|---|
 | 37 | +	}  | 
|---|
 | 38 | +	  | 
|---|
 | 39 | +	public Date toDate()  | 
|---|
 | 40 | +	{  | 
|---|
 | 41 | +		return new Date(time);  | 
|---|
 | 42 | +	}  | 
|---|
 | 43 | +	  | 
|---|
 | 44 | +	public boolean after(RoughTime t)  | 
|---|
 | 45 | +	{  | 
|---|
 | 46 | +		return t.time<time;  | 
|---|
 | 47 | +	}  | 
|---|
 | 48 | +	  | 
|---|
 | 49 | +	public boolean before(RoughTime t)  | 
|---|
 | 50 | +	{  | 
|---|
 | 51 | +		return t.time>time;  | 
|---|
 | 52 | +	}  | 
|---|
 | 53 | +	  | 
|---|
 | 54 | +	public RoughTime plus(int numUnits)  | 
|---|
 | 55 | +	{  | 
|---|
 | 56 | +		return plus(units, numUnits);  | 
|---|
 | 57 | +	}  | 
|---|
 | 58 | +	  | 
|---|
 | 59 | +	public RoughTime plus(TimeUnit unit, int times)  | 
|---|
 | 60 | +	{  | 
|---|
 | 61 | +		RoughTime r=copy();  | 
|---|
 | 62 | +		unit.addTo(r,times);  | 
|---|
 | 63 | +		return r;  | 
|---|
 | 64 | +	}  | 
|---|
 | 65 | +	  | 
|---|
 | 66 | +	public String toString()  | 
|---|
 | 67 | +	{  | 
|---|
 | 68 | +		if (isKnown())  | 
|---|
 | 69 | +			return new Date(time).toString();  | 
|---|
 | 70 | +		return "unknown";  | 
|---|
 | 71 | +	}  | 
|---|
 | 72 | +	  | 
|---|
 | 73 | +	public boolean isKnown()  | 
|---|
 | 74 | +	{  | 
|---|
 | 75 | +		return time!=UNKNOWN;  | 
|---|
 | 76 | +	}  | 
|---|
 | 77 | +	  | 
|---|
 | 78 | +	public boolean equals(Object o)  | 
|---|
 | 79 | +	{  | 
|---|
 | 80 | +		if (!(o instanceof RoughTime))  | 
|---|
 | 81 | +			return false;  | 
|---|
 | 82 | +		RoughTime t=(RoughTime)o;  | 
|---|
 | 83 | +		return t.units==units  && t.time==time;  | 
|---|
 | 84 | +	}  | 
|---|
 | 85 | +	  | 
|---|
 | 86 | +	public RoughTime copy()  | 
|---|
 | 87 | +	{  | 
|---|
 | 88 | +		RoughTime t=new RoughTime(time, units);  | 
|---|
 | 89 | +		return t;  | 
|---|
 | 90 | +	}  | 
|---|
 | 91 | +	  | 
|---|
 | 92 | +	public void setUnits(TimeUnit units)  | 
|---|
 | 93 | +	{  | 
|---|
 | 94 | +		this.units=units;  | 
|---|
 | 95 | +	}  | 
|---|
 | 96 | +  | 
|---|
 | 97 | +	public TimeUnit getUnits() {  | 
|---|
 | 98 | +		return units;  | 
|---|
 | 99 | +	}  | 
|---|
 | 100 | +	  | 
|---|
 | 101 | +	public String format()  | 
|---|
 | 102 | +	{  | 
|---|
 | 103 | +		return units.formatFull(time);  | 
|---|
 | 104 | +	}  | 
|---|
 | 105 | +	  | 
|---|
 | 106 | +	public static int compare(RoughTime t1, RoughTime t2)  | 
|---|
 | 107 | +	{  | 
|---|
 | 108 | +		if (t1==t2)  | 
|---|
 | 109 | +			return 0;  | 
|---|
 | 110 | +		if (t1==null)  | 
|---|
 | 111 | +			return -1;  | 
|---|
 | 112 | +		if (t2==null)  | 
|---|
 | 113 | +			return 1;  | 
|---|
 | 114 | +		long dt= t1.time-t2.time;  | 
|---|
 | 115 | +		if (dt==0)  | 
|---|
 | 116 | +			return 0;  | 
|---|
 | 117 | +		if (dt>0)  | 
|---|
 | 118 | +			return 1;  | 
|---|
 | 119 | +		return -1;  | 
|---|
 | 120 | +	}  | 
|---|
 | 121 | +  | 
|---|
 | 122 | +	@Override  | 
|---|
 | 123 | +	public int compareTo(Object o) {  | 
|---|
 | 124 | +		return compare(this, (RoughTime)o);  | 
|---|
 | 125 | +	}  | 
|---|
 | 126 | +  | 
|---|
 | 127 | +  | 
|---|
 | 128 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.data.time;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import java.util.*;  | 
|---|
 | 4 | +import java.text.*;  | 
|---|
 | 5 | +  | 
|---|
 | 6 | +public class TimeUnit {  | 
|---|
 | 7 | +  | 
|---|
 | 8 | +	public static final TimeUnit YEAR=new TimeUnit("Years", Calendar.YEAR, 365*24*60*60*1000L, "yyyy", "yyyy");  | 
|---|
 | 9 | +	public static final TimeUnit MONTH=new TimeUnit("Months", Calendar.MONTH, 30*24*60*60*1000L, "MMM", "MMM yyyy");  | 
|---|
 | 10 | +	public static final TimeUnit WEEK=new TimeUnit("Weeks", Calendar.WEEK_OF_YEAR, 7*24*60*60*1000L, "d", "MMM d yyyy");  | 
|---|
 | 11 | +	public static final TimeUnit DAY=new TimeUnit("Days", Calendar.DAY_OF_MONTH, 24*60*60*1000L, "d", "MMM d yyyy");  | 
|---|
 | 12 | +	public static final TimeUnit DAY_OF_WEEK=new TimeUnit("Days", Calendar.DAY_OF_WEEK, 24*60*60*1000L, "d", "MMM d yyyy");  | 
|---|
 | 13 | +	public static final TimeUnit HOUR=new TimeUnit("Hours", Calendar.HOUR_OF_DAY, 60*60*1000L, "kk:mm", "MMM d yyyy kk:mm");  | 
|---|
 | 14 | +	public static final TimeUnit MINUTE=new TimeUnit("Minutes", Calendar.MINUTE, 60*1000L, ":mm", "MMM d yyyy kk:mm");  | 
|---|
 | 15 | +	public static final TimeUnit SECOND=new TimeUnit("Seconds", Calendar.SECOND, 1000L, ":ss", "MMM d yyyy kk:mm:ss");  | 
|---|
 | 16 | +	public static final TimeUnit DECADE=multipleYears(10);  | 
|---|
 | 17 | +	public static final TimeUnit CENTURY=multipleYears(100);  | 
|---|
 | 18 | +	  | 
|---|
 | 19 | +	private static final double DAY_SIZE=24*60*60*1000L;  | 
|---|
 | 20 | +	  | 
|---|
 | 21 | +	private int quantity;	  | 
|---|
 | 22 | +	private long roughSize;  | 
|---|
 | 23 | +	private SimpleDateFormat format, fullFormat;  | 
|---|
 | 24 | +	private String name;  | 
|---|
 | 25 | +	private int calendarCode;  | 
|---|
 | 26 | +	  | 
|---|
 | 27 | +	private TimeUnit()  | 
|---|
 | 28 | +	{		  | 
|---|
 | 29 | +	}  | 
|---|
 | 30 | +	  | 
|---|
 | 31 | +	private TimeUnit(String name, int calendarCode, long roughSize, String formatPattern, String fullFormatPattern)  | 
|---|
 | 32 | +	{  | 
|---|
 | 33 | +		this.name=name;  | 
|---|
 | 34 | +		this.calendarCode=calendarCode;  | 
|---|
 | 35 | +		this.roughSize=roughSize;  | 
|---|
 | 36 | +		format=new SimpleDateFormat(formatPattern);  | 
|---|
 | 37 | +		fullFormat=new SimpleDateFormat(fullFormatPattern);  | 
|---|
 | 38 | +		quantity=1;  | 
|---|
 | 39 | +	}  | 
|---|
 | 40 | +	  | 
|---|
 | 41 | +	public String toString()  | 
|---|
 | 42 | +	{  | 
|---|
 | 43 | +		return "[TimeUnit: "+name+"]";  | 
|---|
 | 44 | +	}  | 
|---|
 | 45 | +  | 
|---|
 | 46 | +	public static TimeUnit multipleYears(int numYears)  | 
|---|
 | 47 | +	{  | 
|---|
 | 48 | +		TimeUnit t=new TimeUnit();  | 
|---|
 | 49 | +		t.name=numYears+" Years";  | 
|---|
 | 50 | +		t.calendarCode=Calendar.YEAR;  | 
|---|
 | 51 | +		t.roughSize=YEAR.roughSize*numYears;  | 
|---|
 | 52 | +		t.format=YEAR.format;  | 
|---|
 | 53 | +		t.fullFormat=YEAR.fullFormat;  | 
|---|
 | 54 | +		t.quantity=numYears;  | 
|---|
 | 55 | +		return t;  | 
|---|
 | 56 | +	}  | 
|---|
 | 57 | +	  | 
|---|
 | 58 | +	public static TimeUnit multipleWeeks(int num)  | 
|---|
 | 59 | +	{  | 
|---|
 | 60 | +		TimeUnit t=new TimeUnit();  | 
|---|
 | 61 | +		t.name=num+" Weeks";  | 
|---|
 | 62 | +		t.calendarCode=Calendar.WEEK_OF_YEAR;  | 
|---|
 | 63 | +		t.roughSize=WEEK.roughSize*num;  | 
|---|
 | 64 | +		t.format=WEEK.format;  | 
|---|
 | 65 | +		t.fullFormat=WEEK.fullFormat;  | 
|---|
 | 66 | +		t.quantity=num;  | 
|---|
 | 67 | +		return t;  | 
|---|
 | 68 | +	}  | 
|---|
 | 69 | +	  | 
|---|
 | 70 | +	public TimeUnit times(int quantity)  | 
|---|
 | 71 | +	{  | 
|---|
 | 72 | +		TimeUnit t=new TimeUnit();  | 
|---|
 | 73 | +		t.name=quantity+" "+this.name;  | 
|---|
 | 74 | +		t.calendarCode=this.calendarCode;  | 
|---|
 | 75 | +		t.roughSize=this.roughSize*quantity;  | 
|---|
 | 76 | +		t.format=this.format;  | 
|---|
 | 77 | +		t.fullFormat=this.fullFormat;  | 
|---|
 | 78 | +		t.quantity=quantity;  | 
|---|
 | 79 | +		return t;  | 
|---|
 | 80 | +		  | 
|---|
 | 81 | +	}  | 
|---|
 | 82 | +  | 
|---|
 | 83 | +	  | 
|---|
 | 84 | +	public int numUnitsIn(TimeUnit u)  | 
|---|
 | 85 | +	{  | 
|---|
 | 86 | +		return (int)Math.round(u.getRoughSize()/(double)getRoughSize());  | 
|---|
 | 87 | +	}  | 
|---|
 | 88 | +	  | 
|---|
 | 89 | +	public boolean isDayOrLess()  | 
|---|
 | 90 | +	{  | 
|---|
 | 91 | +		return roughSize <= 24*60*60*1000L;  | 
|---|
 | 92 | +	}  | 
|---|
 | 93 | +	  | 
|---|
 | 94 | +	public RoughTime roundDown(long timestamp)  | 
|---|
 | 95 | +	{  | 
|---|
 | 96 | +		return round(timestamp, false);  | 
|---|
 | 97 | +	}  | 
|---|
 | 98 | +	  | 
|---|
 | 99 | +	public RoughTime roundUp(long timestamp)  | 
|---|
 | 100 | +	{  | 
|---|
 | 101 | +		return round(timestamp, true);  | 
|---|
 | 102 | +	}  | 
|---|
 | 103 | +	  | 
|---|
 | 104 | +	private static final int[] calendarUnits={Calendar.SECOND, Calendar.MINUTE, Calendar.HOUR_OF_DAY, Calendar.DAY_OF_MONTH, Calendar.MONTH, Calendar.YEAR};  | 
|---|
 | 105 | +	public RoughTime round(long timestamp, boolean up)  | 
|---|
 | 106 | +	{  | 
|---|
 | 107 | +		Calendar c=TimeUtils.cal(timestamp);  | 
|---|
 | 108 | +		  | 
|---|
 | 109 | +		if (calendarCode==Calendar.WEEK_OF_YEAR )  | 
|---|
 | 110 | +		{  | 
|---|
 | 111 | +			c.set(Calendar.DAY_OF_WEEK, c.getMinimum(Calendar.DAY_OF_WEEK));  | 
|---|
 | 112 | +		}  | 
|---|
 | 113 | +		else  | 
|---|
 | 114 | +		{  | 
|---|
 | 115 | +		  | 
|---|
 | 116 | +			// set to minimum all fields of finer granularity.  | 
|---|
 | 117 | +			int roundingCode=calendarCode;  | 
|---|
 | 118 | +			if (calendarCode==Calendar.WEEK_OF_YEAR || calendarCode==Calendar.DAY_OF_WEEK)  | 
|---|
 | 119 | +				roundingCode=Calendar.DAY_OF_MONTH;  | 
|---|
 | 120 | +			for (int i=0; i<calendarUnits.length; i++)  | 
|---|
 | 121 | +			{  | 
|---|
 | 122 | +				if (calendarUnits[i]==roundingCode)  | 
|---|
 | 123 | +					break;  | 
|---|
 | 124 | +				if (i==calendarUnits.length-1)  | 
|---|
 | 125 | +					throw new IllegalArgumentException("Unsupported Calendar Unit: "+calendarCode);  | 
|---|
 | 126 | +				c.set(calendarUnits[i], c.getMinimum(calendarUnits[i]));  | 
|---|
 | 127 | +			}  | 
|---|
 | 128 | +			if (quantity>1)  | 
|---|
 | 129 | +			{  | 
|---|
 | 130 | +				c.set(calendarCode, quantity*(c.get(calendarCode)/quantity));  | 
|---|
 | 131 | +			}  | 
|---|
 | 132 | +		}  | 
|---|
 | 133 | +		  | 
|---|
 | 134 | +		// if rounding up, then add a unit at current granularity.  | 
|---|
 | 135 | +		if (up)  | 
|---|
 | 136 | +			c.add(calendarCode, quantity);  | 
|---|
 | 137 | +		  | 
|---|
 | 138 | +		return new RoughTime(c.getTimeInMillis(), this);  | 
|---|
 | 139 | +	}  | 
|---|
 | 140 | +	  | 
|---|
 | 141 | +	public int get(long timestamp)  | 
|---|
 | 142 | +	{  | 
|---|
 | 143 | +		Calendar c= TimeUtils.cal(timestamp);  | 
|---|
 | 144 | +		int n=c.get(calendarCode);  | 
|---|
 | 145 | +		return quantity==1 ? n : n%quantity;  | 
|---|
 | 146 | +	}  | 
|---|
 | 147 | +	  | 
|---|
 | 148 | +	public void addTo(RoughTime r)  | 
|---|
 | 149 | +	{  | 
|---|
 | 150 | +		addTo(r,1);  | 
|---|
 | 151 | +	}  | 
|---|
 | 152 | +	  | 
|---|
 | 153 | +	public void addTo(RoughTime r, int times)  | 
|---|
 | 154 | +	{  | 
|---|
 | 155 | +		Calendar c=TimeUtils.cal(r.getTime());  | 
|---|
 | 156 | +		c.add(calendarCode, quantity*times);  | 
|---|
 | 157 | +		r.setTime(c.getTimeInMillis());  | 
|---|
 | 158 | +	}  | 
|---|
 | 159 | +	  | 
|---|
 | 160 | +	// Finding the difference between two dates, in a given unit of time,  | 
|---|
 | 161 | +	// is much subtler than you'd think! And annoyingly, the Calendar class does not do  | 
|---|
 | 162 | +	// this for you, even though it actually "knows" how to do so since it  | 
|---|
 | 163 | +	// can add fields.  | 
|---|
 | 164 | +	//  | 
|---|
 | 165 | +	// The most vexing problem is dealing with daylight savings time,  | 
|---|
 | 166 | +	// which means that one day a year has 23 hours and one day has 25 hours.  | 
|---|
 | 167 | +	// We also have to handle the fact that months and years aren't constant lengths.  | 
|---|
 | 168 | +	//  | 
|---|
 | 169 | +	// Rather than write all this ourselves, in this code we  | 
|---|
 | 170 | +	// use the Calendar class to do the heavy lifting.  | 
|---|
 | 171 | +	public long difference(long x, long y)  | 
|---|
 | 172 | +	{  | 
|---|
 | 173 | +		// If this is not one of the hard cases,  | 
|---|
 | 174 | +		// just divide the timespan by the length of time unit.  | 
|---|
 | 175 | +		// Note that we're not worrying about hours and daylight savings time.  | 
|---|
 | 176 | +		if (calendarCode!=Calendar.YEAR && calendarCode!=Calendar.MONTH &&   | 
|---|
 | 177 | +		   calendarCode!=Calendar.DAY_OF_MONTH && calendarCode!=Calendar.DAY_OF_WEEK &&  | 
|---|
 | 178 | +		   calendarCode!=Calendar.WEEK_OF_YEAR)  | 
|---|
 | 179 | +		{  | 
|---|
 | 180 | +			return (x-y)/roughSize;  | 
|---|
 | 181 | +		}  | 
|---|
 | 182 | +			  | 
|---|
 | 183 | +		Calendar c1=TimeUtils.cal(x), c2=TimeUtils.cal(y);   | 
|---|
 | 184 | +		int diff=0;  | 
|---|
 | 185 | +		switch (calendarCode)  | 
|---|
 | 186 | +		{  | 
|---|
 | 187 | +			case Calendar.YEAR:  | 
|---|
 | 188 | +				return (c1.get(Calendar.YEAR)-c2.get(Calendar.YEAR))/quantity;  | 
|---|
 | 189 | +				  | 
|---|
 | 190 | +			case Calendar.MONTH:  | 
|---|
 | 191 | +				diff= 12*(c1.get(Calendar.YEAR)-c2.get(Calendar.YEAR))+  | 
|---|
 | 192 | +				              c1.get(Calendar.MONTH)-c2.get(Calendar.MONTH);  | 
|---|
 | 193 | +				return diff/quantity;  | 
|---|
 | 194 | +				  | 
|---|
 | 195 | +			case Calendar.DAY_OF_MONTH:  | 
|---|
 | 196 | +			case Calendar.DAY_OF_WEEK:  | 
|---|
 | 197 | +			case Calendar.DAY_OF_YEAR:  | 
|---|
 | 198 | +			case Calendar.WEEK_OF_MONTH:  | 
|---|
 | 199 | +			case Calendar.WEEK_OF_YEAR:  | 
|---|
 | 200 | +				// This is ugly, but believe me, it beats the alternative methods :-)  | 
|---|
 | 201 | +				// We use the Calendar class's knowledge of daylight savings time.  | 
|---|
 | 202 | +				// and also the fact that if we calculate this naively, then we aren't going  | 
|---|
 | 203 | +				// to be off by more than one in either direction.  | 
|---|
 | 204 | +				int naive=(int)Math.round((x-y)/(double)roughSize);  | 
|---|
 | 205 | +				c2.add(calendarCode, naive*quantity);  | 
|---|
 | 206 | +				if (c1.get(calendarCode)==c2.get(calendarCode))  | 
|---|
 | 207 | +					return naive/quantity;  | 
|---|
 | 208 | +				c2.add(calendarCode, quantity);  | 
|---|
 | 209 | +				if (c1.get(calendarCode)==c2.get(calendarCode))  | 
|---|
 | 210 | +					return naive/quantity+1;  | 
|---|
 | 211 | +				return naive/quantity-1;  | 
|---|
 | 212 | +		}  | 
|---|
 | 213 | +		throw new IllegalArgumentException("Unexpected calendar code: "+calendarCode);  | 
|---|
 | 214 | +	}  | 
|---|
 | 215 | +  | 
|---|
 | 216 | +	public long approxNumInRange(long start, long end)  | 
|---|
 | 217 | +	{  | 
|---|
 | 218 | +		return 1+(end-start)/roughSize;  | 
|---|
 | 219 | +	}  | 
|---|
 | 220 | +	  | 
|---|
 | 221 | +	public long getRoughSize() {  | 
|---|
 | 222 | +		return roughSize;  | 
|---|
 | 223 | +	}  | 
|---|
 | 224 | +  | 
|---|
 | 225 | +	public String format(Date date)  | 
|---|
 | 226 | +	{  | 
|---|
 | 227 | +		return format.format(date);  | 
|---|
 | 228 | +	}  | 
|---|
 | 229 | +  | 
|---|
 | 230 | +	public String formatFull(Date date)  | 
|---|
 | 231 | +	{  | 
|---|
 | 232 | +		return fullFormat.format(date);  | 
|---|
 | 233 | +	}  | 
|---|
 | 234 | +  | 
|---|
 | 235 | +	public String formatFull(long timestamp)  | 
|---|
 | 236 | +	{  | 
|---|
 | 237 | +		return fullFormat.format(new Date(timestamp));  | 
|---|
 | 238 | +	}  | 
|---|
 | 239 | +  | 
|---|
 | 240 | +	public String getName() {  | 
|---|
 | 241 | +		return name;  | 
|---|
 | 242 | +	}  | 
|---|
 | 243 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.data.time;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import java.util.*;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +public class TimeUtils {  | 
|---|
 | 6 | +	  | 
|---|
 | 7 | +	public static Calendar cal(long time)  | 
|---|
 | 8 | +	{  | 
|---|
 | 9 | +		Calendar c=new GregorianCalendar();  | 
|---|
 | 10 | +		c.setTimeInMillis(time);  | 
|---|
 | 11 | +		return c;  | 
|---|
 | 12 | +	}  | 
|---|
 | 13 | +  | 
|---|
 | 14 | +	public static Calendar cal(Date date)  | 
|---|
 | 15 | +	{  | 
|---|
 | 16 | +		Calendar c=new GregorianCalendar();  | 
|---|
 | 17 | +		c.setTime(date);  | 
|---|
 | 18 | +		return c;  | 
|---|
 | 19 | +	}  | 
|---|
 | 20 | +	  | 
|---|
 | 21 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.format.field;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import java.text.*;  | 
|---|
 | 4 | +import java.util.*;  | 
|---|
 | 5 | +  | 
|---|
 | 6 | +import timeflow.data.time.*;  | 
|---|
 | 7 | +  | 
|---|
 | 8 | +public class DateTimeGuesser {  | 
|---|
 | 9 | +	  | 
|---|
 | 10 | +	private DateTimeParser lastGoodFormat;  | 
|---|
 | 11 | +	  | 
|---|
 | 12 | +	private static List<DateTimeParser> parsers=new ArrayList<DateTimeParser>();  | 
|---|
 | 13 | +  | 
|---|
 | 14 | +	/*  | 
|---|
 | 15 | +	   | 
|---|
 | 16 | +	HANDY REFERENCE FOR SIMPLEDATEFORMAT:  | 
|---|
 | 17 | +	(quoted from Java documentation)  | 
|---|
 | 18 | +		   | 
|---|
 | 19 | +	Letter	Date or Time Component	Presentation	Examples  | 
|---|
 | 20 | +	G	Era designator	Text	AD  | 
|---|
 | 21 | +	y	Year	Year	1996; 96  | 
|---|
 | 22 | +	M	Month in year	Month	July; Jul; 07  | 
|---|
 | 23 | +	w	Week in year	Number	27  | 
|---|
 | 24 | +	W	Week in month	Number	2  | 
|---|
 | 25 | +	D	Day in year	Number	189  | 
|---|
 | 26 | +	d	Day in month	Number	10  | 
|---|
 | 27 | +	F	Day of week in month	Number	2  | 
|---|
 | 28 | +	E	Day in week	Text	Tuesday; Tue  | 
|---|
 | 29 | +	a	Am/pm marker	Text	PM  | 
|---|
 | 30 | +	H	Hour in day (0-23)	Number	0  | 
|---|
 | 31 | +	k	Hour in day (1-24)	Number	24  | 
|---|
 | 32 | +	K	Hour in am/pm (0-11)	Number	0  | 
|---|
 | 33 | +	h	Hour in am/pm (1-12)	Number	12  | 
|---|
 | 34 | +	m	Minute in hour	Number	30  | 
|---|
 | 35 | +	s	Second in minute	Number	55  | 
|---|
 | 36 | +	S	Millisecond	Number	978  | 
|---|
 | 37 | +	z	Time zone	General time zone	Pacific Standard Time; PST; GMT-08:00  | 
|---|
 | 38 | +	Z	Time zone	RFC 822 time zone	-0800  | 
|---|
 | 39 | +		   | 
|---|
 | 40 | +		 */  | 
|---|
 | 41 | +		  | 
|---|
 | 42 | +	  | 
|---|
 | 43 | +	// the order of the list below matters--better not to put the year-only ones at the top,  | 
|---|
 | 44 | +	// because then the guesser succeeds before it has a chance to try parsing days.  | 
|---|
 | 45 | +	static  | 
|---|
 | 46 | +	{  | 
|---|
 | 47 | +		parsers.add(new DateTimeParser("yyyy-MM-ddzzzzzzzzzz", TimeUnit.DAY));  | 
|---|
 | 48 | +		parsers.add(new DateTimeParser("MMM dd yyyy HH:mm", TimeUnit.SECOND));  | 
|---|
 | 49 | +		parsers.add(new DateTimeParser("MMM/dd/yyyy HH:mm", TimeUnit.SECOND));  | 
|---|
 | 50 | +		parsers.add(new DateTimeParser("MM/dd/yy HH:mm", TimeUnit.SECOND));  | 
|---|
 | 51 | +		parsers.add(new DateTimeParser("MMM dd yyyy HH:mm:ss", TimeUnit.SECOND));  | 
|---|
 | 52 | +		parsers.add(new DateTimeParser("MM/dd/yyyy HH:mm:ss", TimeUnit.SECOND));  | 
|---|
 | 53 | +		parsers.add(new DateTimeParser("MMM dd yyyy HH:mm:ss zzzzzzzz", TimeUnit.SECOND));  | 
|---|
 | 54 | +		parsers.add(new DateTimeParser("EEE MMM dd HH:mm:ss zzzzzzzz yyyy", TimeUnit.SECOND));  | 
|---|
 | 55 | +		parsers.add(new DateTimeParser("EEE MMM dd HH:mm:ss zzzzzzzz yyyy", TimeUnit.SECOND));  | 
|---|
 | 56 | +		parsers.add(new DateTimeParser("MM-dd-yyyy", TimeUnit.DAY));  | 
|---|
 | 57 | +		parsers.add(new DateTimeParser("yyyy-MM-dd", TimeUnit.DAY));  | 
|---|
 | 58 | +		parsers.add(new DateTimeParser("yyyyMMdd", TimeUnit.DAY));  | 
|---|
 | 59 | +		parsers.add(new DateTimeParser("MM-dd-yy", TimeUnit.DAY));  | 
|---|
 | 60 | +		parsers.add(new DateTimeParser("dd-MMM-yy", TimeUnit.DAY));  | 
|---|
 | 61 | +		parsers.add(new DateTimeParser("MM-dd-yyyy", TimeUnit.DAY));  | 
|---|
 | 62 | +		parsers.add(new DateTimeParser("MM/dd/yy", TimeUnit.DAY));  | 
|---|
 | 63 | +		parsers.add(new DateTimeParser("MM/dd/yyyy", TimeUnit.DAY));  | 
|---|
 | 64 | +		parsers.add(new DateTimeParser("dd MMM yyyy", TimeUnit.DAY));  | 
|---|
 | 65 | +		parsers.add(new DateTimeParser("dd MMM, yyyy", TimeUnit.DAY));  | 
|---|
 | 66 | +		parsers.add(new DateTimeParser("MMM dd yyyy", TimeUnit.DAY));  | 
|---|
 | 67 | +		parsers.add(new DateTimeParser("MMM dd, yyyy", TimeUnit.DAY));  | 
|---|
 | 68 | +		parsers.add(new DateTimeParser("EEE MMM dd zzzzzzzz yyyy", TimeUnit.DAY));  | 
|---|
 | 69 | +		parsers.add(new DateTimeParser("EEE MMM dd yyyy", TimeUnit.DAY));  | 
|---|
 | 70 | +		parsers.add(new DateTimeParser("MMM-yy", TimeUnit.MONTH));  | 
|---|
 | 71 | +		parsers.add(new DateTimeParser("MMM yy", TimeUnit.MONTH));  | 
|---|
 | 72 | +		parsers.add(new DateTimeParser("MMM/yy", TimeUnit.MONTH));  | 
|---|
 | 73 | +		parsers.add(new DateTimeParser("yyyy", TimeUnit.YEAR));  | 
|---|
 | 74 | +		parsers.add(new DateTimeParser("yyyy GG", TimeUnit.YEAR));  | 
|---|
 | 75 | +	}  | 
|---|
 | 76 | +	  | 
|---|
 | 77 | +	public DateTimeParser getLastGoodFormat()  | 
|---|
 | 78 | +	{  | 
|---|
 | 79 | +		return lastGoodFormat;  | 
|---|
 | 80 | +	}  | 
|---|
 | 81 | +  | 
|---|
 | 82 | +	public RoughTime guess(String s)  | 
|---|
 | 83 | +	{  | 
|---|
 | 84 | +		// old code for trying the last good parser.  | 
|---|
 | 85 | +		// we took this out because if the last good one was for a single year,  | 
|---|
 | 86 | +		// but a new one is for years and days,  | 
|---|
 | 87 | +		// all you get is the year.  | 
|---|
 | 88 | +		  | 
|---|
 | 89 | +		//if (lastGoodFormat!=null)  | 
|---|
 | 90 | +		//try { return lastGoodFormat.parse(s); }  | 
|---|
 | 91 | +		//catch (ParseException e) {}	  | 
|---|
 | 92 | +		if (s==null || s.trim().length()==0)  | 
|---|
 | 93 | +			return null;  | 
|---|
 | 94 | +		for (DateTimeParser d: parsers)  | 
|---|
 | 95 | +		{  | 
|---|
 | 96 | +			try  | 
|---|
 | 97 | +			{  | 
|---|
 | 98 | +				RoughTime date= d.parse(s);  | 
|---|
 | 99 | +				lastGoodFormat=d;  | 
|---|
 | 100 | +				return date;  | 
|---|
 | 101 | +			}  | 
|---|
 | 102 | +			catch (ParseException e) {}  | 
|---|
 | 103 | +		}  | 
|---|
 | 104 | +		throw new IllegalArgumentException("Couldn't guess date: '"+s+"'");  | 
|---|
 | 105 | +	}  | 
|---|
 | 106 | +	  | 
|---|
 | 107 | +	public static void main(String[] args)  | 
|---|
 | 108 | +	{  | 
|---|
 | 109 | +		DateTimeGuesser g=new DateTimeGuesser();  | 
|---|
 | 110 | +		System.out.println(g.guess("2009-03-04"));  | 
|---|
 | 111 | +		System.out.println(g.guess("June 10, 2010"));  | 
|---|
 | 112 | +		System.out.println(g.guess("2010"));  | 
|---|
 | 113 | +		System.out.println(g.guess("3/17/10"));  | 
|---|
 | 114 | +	}  | 
|---|
 | 115 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.format.field;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.data.time.*;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +import java.util.*;  | 
|---|
 | 6 | +import java.text.*;  | 
|---|
 | 7 | +  | 
|---|
 | 8 | +  | 
|---|
 | 9 | +// Guesses date format and returns result  | 
|---|
 | 10 | +public class DateTimeParser {  | 
|---|
 | 11 | +	  | 
|---|
 | 12 | +	private DateFormat format;  | 
|---|
 | 13 | +	private TimeUnit units;  | 
|---|
 | 14 | +	private String pattern;  | 
|---|
 | 15 | +  | 
|---|
 | 16 | +	public DateTimeParser(String pattern, TimeUnit units)  | 
|---|
 | 17 | +	{  | 
|---|
 | 18 | +		this.pattern=pattern;  | 
|---|
 | 19 | +		format=new SimpleDateFormat(pattern);  | 
|---|
 | 20 | +		this.units=units;  | 
|---|
 | 21 | +	}  | 
|---|
 | 22 | +	  | 
|---|
 | 23 | +	public RoughTime parse(String s) throws ParseException  | 
|---|
 | 24 | +	{  | 
|---|
 | 25 | +		RoughTime f= new RoughTime(format.parse(s).getTime(), units);  | 
|---|
 | 26 | +		return f;  | 
|---|
 | 27 | +	}  | 
|---|
 | 28 | +	  | 
|---|
 | 29 | +	public TimeUnit getUnits()  | 
|---|
 | 30 | +	{  | 
|---|
 | 31 | +		return units;  | 
|---|
 | 32 | +	}  | 
|---|
 | 33 | +	  | 
|---|
 | 34 | +	public String toString()  | 
|---|
 | 35 | +	{  | 
|---|
 | 36 | +		return "DateParser: pattern="+pattern+", units="+units;  | 
|---|
 | 37 | +	}  | 
|---|
 | 38 | +	  | 
|---|
 | 39 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.format.field;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import java.net.URL;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +import timeflow.data.time.*;  | 
|---|
 | 6 | +  | 
|---|
 | 7 | +public abstract class FieldFormat {	  | 
|---|
 | 8 | +	protected String lastInput;  | 
|---|
 | 9 | +	protected Object lastValue;  | 
|---|
 | 10 | +	protected boolean understood=true;  | 
|---|
 | 11 | +	  | 
|---|
 | 12 | +	double value;  | 
|---|
 | 13 | +	  | 
|---|
 | 14 | +	void add(double x)  | 
|---|
 | 15 | +	{  | 
|---|
 | 16 | +		value+=x;  | 
|---|
 | 17 | +	}  | 
|---|
 | 18 | +	  | 
|---|
 | 19 | +	void note(String s)  | 
|---|
 | 20 | +	{  | 
|---|
 | 21 | +		add(scoreFormatMatch(s));  | 
|---|
 | 22 | +	}  | 
|---|
 | 23 | +  | 
|---|
 | 24 | +	  | 
|---|
 | 25 | +	protected abstract Object _parse(String s) throws Exception;  | 
|---|
 | 26 | +	public abstract String format(Object o);  | 
|---|
 | 27 | +	public abstract Class getType();  | 
|---|
 | 28 | +	public abstract double scoreFormatMatch(String s);  | 
|---|
 | 29 | +	public abstract String getHumanName();  | 
|---|
 | 30 | +  | 
|---|
 | 31 | +  | 
|---|
 | 32 | +	public void setValue(Object o)  | 
|---|
 | 33 | +	{  | 
|---|
 | 34 | +		lastValue=o;  | 
|---|
 | 35 | +		lastInput=o==null ? "" : format(o);  | 
|---|
 | 36 | +	}  | 
|---|
 | 37 | +	  | 
|---|
 | 38 | +	public Object parse(String s) throws Exception  | 
|---|
 | 39 | +	{  | 
|---|
 | 40 | +		lastInput=s;  | 
|---|
 | 41 | +		lastValue=null;  | 
|---|
 | 42 | +		understood=false;  | 
|---|
 | 43 | +		lastValue=_parse(s);  | 
|---|
 | 44 | +		understood=true;  | 
|---|
 | 45 | +		return lastValue;  | 
|---|
 | 46 | +	}  | 
|---|
 | 47 | +	  | 
|---|
 | 48 | +	public Object getLastValue()  | 
|---|
 | 49 | +	{  | 
|---|
 | 50 | +		return lastValue;  | 
|---|
 | 51 | +	}  | 
|---|
 | 52 | +	  | 
|---|
 | 53 | +	public String feedback()   | 
|---|
 | 54 | +	{  | 
|---|
 | 55 | +		if (!understood)  | 
|---|
 | 56 | +			return "Couldn't understand";  | 
|---|
 | 57 | +		return lastValue==null ? "(missing)" : "Read: "+format(lastValue);  | 
|---|
 | 58 | +	}  | 
|---|
 | 59 | +	  | 
|---|
 | 60 | +	public boolean isUnderstood()  | 
|---|
 | 61 | +	{  | 
|---|
 | 62 | +		return understood;  | 
|---|
 | 63 | +	}  | 
|---|
 | 64 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.format.field;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.data.time.*;  | 
|---|
 | 4 | +import timeflow.util.*;  | 
|---|
 | 5 | +  | 
|---|
 | 6 | +import java.net.URL;  | 
|---|
 | 7 | +import java.util.*;  | 
|---|
 | 8 | +  | 
|---|
 | 9 | +public class FieldFormatCatalog {  | 
|---|
 | 10 | +  | 
|---|
 | 11 | +	private static Map<String, FieldFormat> formatTable=new HashMap<String, FieldFormat>();  | 
|---|
 | 12 | +	private static Map<Class, FieldFormat> classTable=new HashMap<Class, FieldFormat>();  | 
|---|
 | 13 | +  | 
|---|
 | 14 | +	static  | 
|---|
 | 15 | +	{  | 
|---|
 | 16 | +		for (FieldFormat f: listFormats())  | 
|---|
 | 17 | +		{  | 
|---|
 | 18 | +			formatTable.put(f.getHumanName(), f);  | 
|---|
 | 19 | +			classTable.put(f.getType(), f);  | 
|---|
 | 20 | +		}  | 
|---|
 | 21 | +	}  | 
|---|
 | 22 | +	  | 
|---|
 | 23 | +	static FieldFormat[] listFormats()  | 
|---|
 | 24 | +	{  | 
|---|
 | 25 | +		return new FieldFormat[] {new FormatDateTime(), new FormatString(),  | 
|---|
 | 26 | +				new FormatStringArray(), new FormatDouble(), new FormatURL()};  | 
|---|
 | 27 | +	}  | 
|---|
 | 28 | +	  | 
|---|
 | 29 | +	public static Iterable<String> classNames()  | 
|---|
 | 30 | +	{  | 
|---|
 | 31 | +		return formatTable.keySet();  | 
|---|
 | 32 | +	}  | 
|---|
 | 33 | +	  | 
|---|
 | 34 | +	public static String humanName(Class c){  | 
|---|
 | 35 | +		return getFormat(c).getHumanName();  | 
|---|
 | 36 | +	}  | 
|---|
 | 37 | +	  | 
|---|
 | 38 | +  | 
|---|
 | 39 | +	public static FieldFormat getFormat(Class c) {  | 
|---|
 | 40 | +		FieldFormat f= classTable.get(c);  | 
|---|
 | 41 | +		if (f==null)  | 
|---|
 | 42 | +			System.out.println("Warning: no FieldFormat for "+c);  | 
|---|
 | 43 | +		return f;  | 
|---|
 | 44 | +	}  | 
|---|
 | 45 | +  | 
|---|
 | 46 | +	  | 
|---|
 | 47 | +	public static Class javaClass(String humanName)  | 
|---|
 | 48 | +	{  | 
|---|
 | 49 | +		Class  c=formatTable.get(humanName).getType();  | 
|---|
 | 50 | +		if (c==null)  | 
|---|
 | 51 | +			System.out.println("Warning: no class for "+humanName);  | 
|---|
 | 52 | +		return c;  | 
|---|
 | 53 | +	}  | 
|---|
 | 54 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.format.field;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.util.*;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +public class FieldFormatGuesser {  | 
|---|
 | 6 | +	  | 
|---|
 | 7 | +	FieldFormat[] scores;  | 
|---|
 | 8 | +	  | 
|---|
 | 9 | +	private FieldFormatGuesser()  | 
|---|
 | 10 | +	{  | 
|---|
 | 11 | +		scores=FieldFormatCatalog.listFormats();  | 
|---|
 | 12 | +	}  | 
|---|
 | 13 | +	public static Class[] analyze(String[][] data, int startRow, int numRows)  | 
|---|
 | 14 | +	{  | 
|---|
 | 15 | +		int n=data[0].length;  | 
|---|
 | 16 | +		FieldFormatGuesser[] g=new FieldFormatGuesser[n];  | 
|---|
 | 17 | +		for (int i=0; i<n; i++)  | 
|---|
 | 18 | +			g[i]=new FieldFormatGuesser();  | 
|---|
 | 19 | +		for (int i=startRow; i<startRow+numRows && i<data.length; i++)  | 
|---|
 | 20 | +		{  | 
|---|
 | 21 | +			for (int j=0; j<n; j++)  | 
|---|
 | 22 | +				g[j].add(data[i][j]);  | 
|---|
 | 23 | +		}  | 
|---|
 | 24 | +		Class[] c=new Class[n];  | 
|---|
 | 25 | +		for (int i=0; i<n; i++)  | 
|---|
 | 26 | +			c[i]=g[i].best();  | 
|---|
 | 27 | +		return c;  | 
|---|
 | 28 | +	}	  | 
|---|
 | 29 | +	  | 
|---|
 | 30 | +	private void add(String s)  | 
|---|
 | 31 | +	{  | 
|---|
 | 32 | +		for (int i=0; i<scores.length; i++)  | 
|---|
 | 33 | +			scores[i].note(s);  | 
|---|
 | 34 | +	}  | 
|---|
 | 35 | +	  | 
|---|
 | 36 | +	private Class best()  | 
|---|
 | 37 | +	{  | 
|---|
 | 38 | +		double max=scores[0].value;  | 
|---|
 | 39 | +		Class best=scores[0].getType();  | 
|---|
 | 40 | +		for (int i=1; i<scores.length; i++)  | 
|---|
 | 41 | +		{  | 
|---|
 | 42 | +			if (scores[i].value>max)  | 
|---|
 | 43 | +			{  | 
|---|
 | 44 | +				max=scores[i].value;  | 
|---|
 | 45 | +				best=scores[i].getType();  | 
|---|
 | 46 | +			}  | 
|---|
 | 47 | +		}  | 
|---|
 | 48 | +		return best;  | 
|---|
 | 49 | +	}  | 
|---|
 | 50 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +/**  | 
|---|
 | 2 | + *   | 
|---|
 | 3 | + */  | 
|---|
 | 4 | +package timeflow.format.field;  | 
|---|
 | 5 | +  | 
|---|
 | 6 | +import java.text.ParseException;  | 
|---|
 | 7 | +import java.util.Calendar;  | 
|---|
 | 8 | +  | 
|---|
 | 9 | +import timeflow.data.time.RoughTime;  | 
|---|
 | 10 | +import timeflow.data.time.TimeUnit;  | 
|---|
 | 11 | +import timeflow.data.time.TimeUtils;  | 
|---|
 | 12 | +  | 
|---|
 | 13 | +public class FormatDateTime extends FieldFormat  | 
|---|
 | 14 | +{  | 
|---|
 | 15 | +	DateTimeGuesser dateGuesser=new DateTimeGuesser();  | 
|---|
 | 16 | +	  | 
|---|
 | 17 | +	@Override  | 
|---|
 | 18 | +	public String format(Object o) {  | 
|---|
 | 19 | +		return ((RoughTime)o).format();  | 
|---|
 | 20 | +	}  | 
|---|
 | 21 | +  | 
|---|
 | 22 | +	@Override  | 
|---|
 | 23 | +	public Object _parse(String s) throws Exception {  | 
|---|
 | 24 | +		if (s.length()==0)  | 
|---|
 | 25 | +			return null;  | 
|---|
 | 26 | +		Object o= readTime(s);  | 
|---|
 | 27 | +		if (o==null)  | 
|---|
 | 28 | +			throw new IllegalArgumentException();  | 
|---|
 | 29 | +		return o;  | 
|---|
 | 30 | +	}  | 
|---|
 | 31 | +	  | 
|---|
 | 32 | +	  | 
|---|
 | 33 | +	public RoughTime readTime(Object o) throws ParseException  | 
|---|
 | 34 | +	{  | 
|---|
 | 35 | +		if (!(o instanceof String))  | 
|---|
 | 36 | +			throw new IllegalArgumentException("Expected String, got: "+o);  | 
|---|
 | 37 | +		return dateGuesser.guess((String)o);  | 
|---|
 | 38 | +	}  | 
|---|
 | 39 | +	DateTimeGuesser g=new DateTimeGuesser();  | 
|---|
 | 40 | +	  | 
|---|
 | 41 | +	@Override  | 
|---|
 | 42 | +	public double scoreFormatMatch(String s) {  | 
|---|
 | 43 | +		if (s==null || s.length()==0)  | 
|---|
 | 44 | +			return -.05;  | 
|---|
 | 45 | +		try  | 
|---|
 | 46 | +		{  | 
|---|
 | 47 | +			RoughTime f=g.guess(s);  | 
|---|
 | 48 | +			if (f==null)  | 
|---|
 | 49 | +				return -1;  | 
|---|
 | 50 | +			if (g.getLastGoodFormat().getUnits()==TimeUnit.YEAR)  | 
|---|
 | 51 | +			{  | 
|---|
 | 52 | +				int year=TimeUtils.cal(f.getTime()).get(Calendar.YEAR);  | 
|---|
 | 53 | +				if (year>2100)  | 
|---|
 | 54 | +					return -1;  | 
|---|
 | 55 | +				if (year>1900 && year<2050)  | 
|---|
 | 56 | +					return 1;  | 
|---|
 | 57 | +				if (year>2050 || year<1600)  | 
|---|
 | 58 | +					return .1;  | 
|---|
 | 59 | +				return .5;  | 
|---|
 | 60 | +			}  | 
|---|
 | 61 | +			return 2;  | 
|---|
 | 62 | +		}  | 
|---|
 | 63 | +		catch (Exception e)  | 
|---|
 | 64 | +		{  | 
|---|
 | 65 | +			return -1;  | 
|---|
 | 66 | +		}  | 
|---|
 | 67 | +	}  | 
|---|
 | 68 | +  | 
|---|
 | 69 | +  | 
|---|
 | 70 | +	@Override  | 
|---|
 | 71 | +	public Class getType() {  | 
|---|
 | 72 | +		return RoughTime.class;  | 
|---|
 | 73 | +	}  | 
|---|
 | 74 | +  | 
|---|
 | 75 | +	@Override  | 
|---|
 | 76 | +	public String getHumanName() {  | 
|---|
 | 77 | +		return "Date/Time";  | 
|---|
 | 78 | +	}		  | 
|---|
 | 79 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +/**  | 
|---|
 | 2 | + *   | 
|---|
 | 3 | + */  | 
|---|
 | 4 | +package timeflow.format.field;  | 
|---|
 | 5 | +  | 
|---|
 | 6 | +public class FormatDouble extends FieldFormat  | 
|---|
 | 7 | +{	  | 
|---|
 | 8 | +	@Override  | 
|---|
 | 9 | +	public String format(Object o) {  | 
|---|
 | 10 | +		return o.toString();  | 
|---|
 | 11 | +	}  | 
|---|
 | 12 | +  | 
|---|
 | 13 | +	@Override  | 
|---|
 | 14 | +	public Object _parse(String s) {  | 
|---|
 | 15 | +		if (s==null || s.trim().length()==0)  | 
|---|
 | 16 | +			return null;  | 
|---|
 | 17 | +		return parseDouble(s);  | 
|---|
 | 18 | +	}  | 
|---|
 | 19 | +	  | 
|---|
 | 20 | +	public static double parseDouble(String s)  | 
|---|
 | 21 | +	{  | 
|---|
 | 22 | +		int n=s.length();  | 
|---|
 | 23 | +		if (n>3)  | 
|---|
 | 24 | +		{  | 
|---|
 | 25 | +			if (s.charAt(0)=='(' && s.charAt(n-1)==')')  | 
|---|
 | 26 | +			{  | 
|---|
 | 27 | +				s='-'+s.substring(1,n-1);  | 
|---|
 | 28 | +				n--;  | 
|---|
 | 29 | +			}  | 
|---|
 | 30 | +		}  | 
|---|
 | 31 | +		// remove $,%, etc.  | 
|---|
 | 32 | +		StringBuffer b=new StringBuffer();  | 
|---|
 | 33 | +		for (int i=0; i<n; i++)  | 
|---|
 | 34 | +		{  | 
|---|
 | 35 | +			char c=s.charAt(i);  | 
|---|
 | 36 | +			if (Character.isDigit(c) || c=='-' || c=='.')  | 
|---|
 | 37 | +				b.append(c);  | 
|---|
 | 38 | +		}  | 
|---|
 | 39 | +		  | 
|---|
 | 40 | +		try  | 
|---|
 | 41 | +		{  | 
|---|
 | 42 | +		return Double.parseDouble(b.toString());  | 
|---|
 | 43 | +		}  | 
|---|
 | 44 | +		catch (RuntimeException e)  | 
|---|
 | 45 | +		{  | 
|---|
 | 46 | +			System.out.println("b="+b);  | 
|---|
 | 47 | +			throw e;  | 
|---|
 | 48 | +		}  | 
|---|
 | 49 | +	}  | 
|---|
 | 50 | +  | 
|---|
 | 51 | +  | 
|---|
 | 52 | +	@Override  | 
|---|
 | 53 | +	public Class getType() {  | 
|---|
 | 54 | +		return Double.class;  | 
|---|
 | 55 | +	}	  | 
|---|
 | 56 | +	@Override  | 
|---|
 | 57 | +	public double scoreFormatMatch(String s) {  | 
|---|
 | 58 | +		s=s.trim();  | 
|---|
 | 59 | +		int n=s.length();  | 
|---|
 | 60 | +		if (n==5) // possible zip code  | 
|---|
 | 61 | +		{  | 
|---|
 | 62 | +			if (s.charAt(0)=='0')  | 
|---|
 | 63 | +				return -3; // gotta be a zip code!  | 
|---|
 | 64 | +			return 0;  | 
|---|
 | 65 | +		}  | 
|---|
 | 66 | +		if (n==9) // possible zip+4, but really, who knows...  | 
|---|
 | 67 | +			return 0;  | 
|---|
 | 68 | +		  | 
|---|
 | 69 | +		if (n==4) // possible date.  | 
|---|
 | 70 | +		{  | 
|---|
 | 71 | +			try  | 
|---|
 | 72 | +			{  | 
|---|
 | 73 | +				int x=Integer.parseInt(s);  | 
|---|
 | 74 | +				if (x>1900 && x<2030)  | 
|---|
 | 75 | +					return -1; // that's very likely a date.  | 
|---|
 | 76 | +				if (x>1700 && x<2100)  | 
|---|
 | 77 | +					return 0; // you don't know.  | 
|---|
 | 78 | +				  | 
|---|
 | 79 | +			}  | 
|---|
 | 80 | +			catch (Exception e) {} // evidently not a date :-)  | 
|---|
 | 81 | +		}  | 
|---|
 | 82 | +		  | 
|---|
 | 83 | +		if (n==0)  | 
|---|
 | 84 | +			return -.1;  | 
|---|
 | 85 | +		int ok=0;  | 
|---|
 | 86 | +		int bad=0;  | 
|---|
 | 87 | +		for (int i=0; i<n; i++)  | 
|---|
 | 88 | +		{  | 
|---|
 | 89 | +			char c=s.charAt(i);  | 
|---|
 | 90 | +			if (Character.isDigit(c) || c=='.' || c==',' || c=='-' || c=='$' || c=='%')  | 
|---|
 | 91 | +				ok++;  | 
|---|
 | 92 | +			else  | 
|---|
 | 93 | +				bad++;  | 
|---|
 | 94 | +		}  | 
|---|
 | 95 | +		return 4-5*bad;  | 
|---|
 | 96 | +	}  | 
|---|
 | 97 | +  | 
|---|
 | 98 | +	@Override  | 
|---|
 | 99 | +	public String getHumanName() {  | 
|---|
 | 100 | +		return "Number";  | 
|---|
 | 101 | +	}  | 
|---|
 | 102 | +  | 
|---|
 | 103 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +/**  | 
|---|
 | 2 | + *   | 
|---|
 | 3 | + */  | 
|---|
 | 4 | +package timeflow.format.field;  | 
|---|
 | 5 | +  | 
|---|
 | 6 | +public class FormatString extends FieldFormat  | 
|---|
 | 7 | +{	  | 
|---|
 | 8 | +	@Override  | 
|---|
 | 9 | +	public String format(Object o) {  | 
|---|
 | 10 | +		return o.toString();  | 
|---|
 | 11 | +	}  | 
|---|
 | 12 | +  | 
|---|
 | 13 | +	@Override  | 
|---|
 | 14 | +	public Object _parse(String s) {  | 
|---|
 | 15 | +		return s;  | 
|---|
 | 16 | +	}		  | 
|---|
 | 17 | +	  | 
|---|
 | 18 | +	  | 
|---|
 | 19 | +	public String feedback()   | 
|---|
 | 20 | +	{  | 
|---|
 | 21 | +		if (lastValue==null)  | 
|---|
 | 22 | +			return "Couldn't understand";  | 
|---|
 | 23 | +		if (((String)lastValue).length()==0)  | 
|---|
 | 24 | +			return "Blank";  | 
|---|
 | 25 | +		return "";  | 
|---|
 | 26 | +	}  | 
|---|
 | 27 | +  | 
|---|
 | 28 | +	@Override  | 
|---|
 | 29 | +	public Class getType() {  | 
|---|
 | 30 | +		return String.class;  | 
|---|
 | 31 | +	}  | 
|---|
 | 32 | +	  | 
|---|
 | 33 | +	@Override  | 
|---|
 | 34 | +	public double scoreFormatMatch(String s) {  | 
|---|
 | 35 | +		return s!=null && s.length()>0 ? .1 : 0;  | 
|---|
 | 36 | +	}  | 
|---|
 | 37 | +  | 
|---|
 | 38 | +	@Override  | 
|---|
 | 39 | +	public String getHumanName() {  | 
|---|
 | 40 | +		return "Text";  | 
|---|
 | 41 | +	}  | 
|---|
 | 42 | +  | 
|---|
 | 43 | +  | 
|---|
 | 44 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +/**  | 
|---|
 | 2 | + *   | 
|---|
 | 3 | + */  | 
|---|
 | 4 | +package timeflow.format.field;  | 
|---|
 | 5 | +  | 
|---|
 | 6 | +import timeflow.model.Display;  | 
|---|
 | 7 | +  | 
|---|
 | 8 | +public class FormatStringArray extends FieldFormat  | 
|---|
 | 9 | +{  | 
|---|
 | 10 | +	@Override  | 
|---|
 | 11 | +	public String format(Object o) {  | 
|---|
 | 12 | +		return Display.arrayToString((String[])o);  | 
|---|
 | 13 | +	}  | 
|---|
 | 14 | +  | 
|---|
 | 15 | +	@Override  | 
|---|
 | 16 | +	public Object _parse(String s) {  | 
|---|
 | 17 | +		return parseList(s);  | 
|---|
 | 18 | +	}	  | 
|---|
 | 19 | +	  | 
|---|
 | 20 | +	public static String[] parseList(String s)  | 
|---|
 | 21 | +	{  | 
|---|
 | 22 | +		String[] t= s.length()==0 ? new String[0] : s.split(",");  | 
|---|
 | 23 | +		for (int i=0; i<t.length; i++)  | 
|---|
 | 24 | +			t[i]=t[i].trim();  | 
|---|
 | 25 | +		return t;  | 
|---|
 | 26 | +	}  | 
|---|
 | 27 | +	  | 
|---|
 | 28 | +	public String feedback()   | 
|---|
 | 29 | +	{  | 
|---|
 | 30 | +		if (lastValue==null)  | 
|---|
 | 31 | +			return "Couldn't understand";  | 
|---|
 | 32 | +		String[] s=(String[])lastValue;  | 
|---|
 | 33 | +		if (s.length==0)  | 
|---|
 | 34 | +			return "Empty list";  | 
|---|
 | 35 | +		if (s.length==1)  | 
|---|
 | 36 | +			return "One item";  | 
|---|
 | 37 | +		return s.length+" items";  | 
|---|
 | 38 | +	}  | 
|---|
 | 39 | +  | 
|---|
 | 40 | +	@Override  | 
|---|
 | 41 | +	public Class getType() {  | 
|---|
 | 42 | +		return String[].class;  | 
|---|
 | 43 | +	}	  | 
|---|
 | 44 | +  | 
|---|
 | 45 | +	@Override  | 
|---|
 | 46 | +	public double scoreFormatMatch(String s) {  | 
|---|
 | 47 | +		double commas=-1;  | 
|---|
 | 48 | +		for (int i=s.length()-1; i>=0; i--)  | 
|---|
 | 49 | +			if (s.charAt(i)==',')  | 
|---|
 | 50 | +				commas++;  | 
|---|
 | 51 | +		return commas/s.length();  | 
|---|
 | 52 | +	}  | 
|---|
 | 53 | +  | 
|---|
 | 54 | +	@Override  | 
|---|
 | 55 | +	public String getHumanName() {  | 
|---|
 | 56 | +		return "List";  | 
|---|
 | 57 | +	}  | 
|---|
 | 58 | +  | 
|---|
 | 59 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +/**  | 
|---|
 | 2 | + *   | 
|---|
 | 3 | + */  | 
|---|
 | 4 | +package timeflow.format.field;  | 
|---|
 | 5 | +  | 
|---|
 | 6 | +import java.net.URL;  | 
|---|
 | 7 | +  | 
|---|
 | 8 | +public class FormatURL extends FieldFormat  | 
|---|
 | 9 | +{	  | 
|---|
 | 10 | +	@Override  | 
|---|
 | 11 | +	public String format(Object o) {  | 
|---|
 | 12 | +		return o.toString();  | 
|---|
 | 13 | +	}  | 
|---|
 | 14 | +  | 
|---|
 | 15 | +	@Override  | 
|---|
 | 16 | +	public Object _parse(String s) throws Exception {  | 
|---|
 | 17 | +		if (s.length()==0)  | 
|---|
 | 18 | +			return null;  | 
|---|
 | 19 | +		return new URL(s);  | 
|---|
 | 20 | +	}  | 
|---|
 | 21 | +  | 
|---|
 | 22 | +	@Override  | 
|---|
 | 23 | +	public Class getType() {  | 
|---|
 | 24 | +		return URL.class;  | 
|---|
 | 25 | +	}	  | 
|---|
 | 26 | +	  | 
|---|
 | 27 | +	@Override  | 
|---|
 | 28 | +	public double scoreFormatMatch(String s) {  | 
|---|
 | 29 | +		if (s==null || s.length()==0)  | 
|---|
 | 30 | +			return 0;  | 
|---|
 | 31 | +		if (s.startsWith("http") || s.startsWith("file://"))  | 
|---|
 | 32 | +			return 5;  | 
|---|
 | 33 | +		return -1;  | 
|---|
 | 34 | +	}  | 
|---|
 | 35 | +  | 
|---|
 | 36 | +	@Override  | 
|---|
 | 37 | +	public String getHumanName() {  | 
|---|
 | 38 | +		return "URL";  | 
|---|
 | 39 | +	}  | 
|---|
 | 40 | +  | 
|---|
 | 41 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.format.file;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import java.io.BufferedOutputStream;  | 
|---|
 | 4 | +import java.io.File;  | 
|---|
 | 5 | +import java.io.FileOutputStream;  | 
|---|
 | 6 | +import java.io.IOException;  | 
|---|
 | 7 | +import java.io.PrintStream;  | 
|---|
 | 8 | +import java.io.PrintWriter;  | 
|---|
 | 9 | +import java.util.ArrayList;  | 
|---|
 | 10 | +import java.util.Arrays;  | 
|---|
 | 11 | +import java.util.Iterator;  | 
|---|
 | 12 | +import java.util.List;  | 
|---|
 | 13 | +  | 
|---|
 | 14 | +import timeflow.data.db.Act;  | 
|---|
 | 15 | +import timeflow.data.db.ActDB;  | 
|---|
 | 16 | +import timeflow.data.db.DBUtils;  | 
|---|
 | 17 | +import timeflow.data.db.Field;  | 
|---|
 | 18 | +import timeflow.data.time.RoughTime;  | 
|---|
 | 19 | +import timeflow.model.Display;  | 
|---|
 | 20 | +  | 
|---|
 | 21 | +import timeflow.util.*;  | 
|---|
 | 22 | +  | 
|---|
 | 23 | +public class DelimitedFormat {  | 
|---|
 | 24 | +	  | 
|---|
 | 25 | +	char delimiter;  | 
|---|
 | 26 | +	DelimitedText delimitedText;  | 
|---|
 | 27 | +  | 
|---|
 | 28 | +	public DelimitedFormat(char delimiter)  | 
|---|
 | 29 | +	{  | 
|---|
 | 30 | +		this.delimiter=delimiter;  | 
|---|
 | 31 | +		delimitedText=new DelimitedText(delimiter);  | 
|---|
 | 32 | +	}  | 
|---|
 | 33 | +  | 
|---|
 | 34 | +	public static String[][] readArrayGuessDelim(String fileName, PrintStream messages) throws Exception  | 
|---|
 | 35 | +	{  | 
|---|
 | 36 | +		//messages.println("DelimitedFormat: reading "+fileName);  | 
|---|
 | 37 | +		String text=IO.read(fileName);  | 
|---|
 | 38 | +		return readArrayFromString(text, messages);		  | 
|---|
 | 39 | +	}  | 
|---|
 | 40 | +	  | 
|---|
 | 41 | +	public static String[][] readArrayFromString(String text, PrintStream messages) throws Exception  | 
|---|
 | 42 | +	{  | 
|---|
 | 43 | +		//messages.println("DelimitedFormat: reading string, length="+text.length());  | 
|---|
 | 44 | +		int n=Math.min(text.length(), 1000);  | 
|---|
 | 45 | +		String beginning=text.substring(0,n);  | 
|---|
 | 46 | +		char c=count(beginning, '\t')>count(beginning, ',') ? '\t' : ',';  | 
|---|
 | 47 | +		return new DelimitedFormat(c).readTokensFromString(text, messages);  | 
|---|
 | 48 | +	}  | 
|---|
 | 49 | +	  | 
|---|
 | 50 | +	private static String[] removeBlankLines(String[] lines)  | 
|---|
 | 51 | +	{  | 
|---|
 | 52 | +		List<String> good=new ArrayList<String>();  | 
|---|
 | 53 | +		for (int i=0; i<lines.length; i++)  | 
|---|
 | 54 | +		{  | 
|---|
 | 55 | +			if (!(lines[i]==null || lines[i].trim().length()==0))  | 
|---|
 | 56 | +				good.add(lines[i]);  | 
|---|
 | 57 | +		}  | 
|---|
 | 58 | +		return (String[])good.toArray(new String[0]);  | 
|---|
 | 59 | +	}  | 
|---|
 | 60 | +	  | 
|---|
 | 61 | +	private static int count(String s, char c)  | 
|---|
 | 62 | +	{  | 
|---|
 | 63 | +		int n=0;  | 
|---|
 | 64 | +		for (int i=0; i<s.length(); i++)  | 
|---|
 | 65 | +		{  | 
|---|
 | 66 | +			if (s.charAt(i)==c)  | 
|---|
 | 67 | +				n++;  | 
|---|
 | 68 | +		}  | 
|---|
 | 69 | +		return n;  | 
|---|
 | 70 | +	}  | 
|---|
 | 71 | +  | 
|---|
 | 72 | +	public String[][] readTokensFromString(String text, PrintStream messages) throws Exception  | 
|---|
 | 73 | +	{  | 
|---|
 | 74 | +		  | 
|---|
 | 75 | +		ArrayList<String[]> resultList=new ArrayList<String[]>();  | 
|---|
 | 76 | +		Iterator<String[]> lines=delimitedText.read(text).iterator();  | 
|---|
 | 77 | +		int numCols=-1;  | 
|---|
 | 78 | +		while(lines.hasNext())  | 
|---|
 | 79 | +		{  | 
|---|
 | 80 | +			String[] r=lines.next();  | 
|---|
 | 81 | +			int ri=r.length;  | 
|---|
 | 82 | +			if (numCols==-1)  | 
|---|
 | 83 | +				numCols=r.length;  | 
|---|
 | 84 | +			else  | 
|---|
 | 85 | +			{  | 
|---|
 | 86 | +				if (ri>numCols)  | 
|---|
 | 87 | +				{  | 
|---|
 | 88 | +					messages.println("Line too long: "+ri+" > "+numCols);  | 
|---|
 | 89 | +					messages.println("line="+Display.arrayToString(r));  | 
|---|
 | 90 | +				}  | 
|---|
 | 91 | +				else if (ri<numCols)  | 
|---|
 | 92 | +				{  | 
|---|
 | 93 | +					String[] old=r;  | 
|---|
 | 94 | +					r=new String[numCols];  | 
|---|
 | 95 | +					System.arraycopy(old,0,r,0,ri);  | 
|---|
 | 96 | +					for (int j=ri; j<numCols; j++)   | 
|---|
 | 97 | +						r[j]="";  | 
|---|
 | 98 | +				}  | 
|---|
 | 99 | +			}  | 
|---|
 | 100 | +			resultList.add(r);  | 
|---|
 | 101 | +		}  | 
|---|
 | 102 | +		//messages.println("# lines read: "+resultList.size());  | 
|---|
 | 103 | +		return (String[][]) resultList.toArray(new String[0][]);  | 
|---|
 | 104 | +	}  | 
|---|
 | 105 | +	  | 
|---|
 | 106 | +	public void write(ActDB db, File file) throws IOException  | 
|---|
 | 107 | +	{  | 
|---|
 | 108 | +		write(db, db, file);  | 
|---|
 | 109 | +	}  | 
|---|
 | 110 | +		  | 
|---|
 | 111 | +	public void write(ActDB db, Iterable<Act> acts, File file) throws IOException  | 
|---|
 | 112 | +	{  | 
|---|
 | 113 | +		FileOutputStream fos=new FileOutputStream(file);  | 
|---|
 | 114 | +		BufferedOutputStream b=new BufferedOutputStream(fos);  | 
|---|
 | 115 | +		PrintStream out=new PrintStream(b);  | 
|---|
 | 116 | +		  | 
|---|
 | 117 | +		// Write data!  | 
|---|
 | 118 | +		writeDelimited(db, acts, out);  | 
|---|
 | 119 | +		  | 
|---|
 | 120 | +		out.flush();  | 
|---|
 | 121 | +		out.close();  | 
|---|
 | 122 | +		b.close();  | 
|---|
 | 123 | +		fos.close();  | 
|---|
 | 124 | +	}  | 
|---|
 | 125 | +  | 
|---|
 | 126 | +	  | 
|---|
 | 127 | +	void writeDelimited(ActDB db, PrintStream out)  | 
|---|
 | 128 | +	{		  | 
|---|
 | 129 | +		writeDelimited(db, db, out);  | 
|---|
 | 130 | +	}  | 
|---|
 | 131 | +	  | 
|---|
 | 132 | +	public void writeDelimited(ActDB db, Iterable<Act> acts, PrintWriter out)  | 
|---|
 | 133 | +	{		  | 
|---|
 | 134 | +		// Write headers		  | 
|---|
 | 135 | +		List<String> names=new ArrayList<String>();  | 
|---|
 | 136 | +		List<Field> fields=db.getFields();  | 
|---|
 | 137 | +		for (Field f: fields)  | 
|---|
 | 138 | +			names.add(f.getName());  | 
|---|
 | 139 | +		print(names, out);  | 
|---|
 | 140 | +		  | 
|---|
 | 141 | +		// Write data  | 
|---|
 | 142 | +		for (Act a: acts)  | 
|---|
 | 143 | +		{  | 
|---|
 | 144 | +			List<String> data=new ArrayList<String>();  | 
|---|
 | 145 | +			for (Field f: fields)  | 
|---|
 | 146 | +				data.add(format(a.get(f)));  | 
|---|
 | 147 | +			print(data, out);  | 
|---|
 | 148 | +		}  | 
|---|
 | 149 | +	}  | 
|---|
 | 150 | +  | 
|---|
 | 151 | +	  | 
|---|
 | 152 | +	public void writeDelimited(ActDB db, Iterable<Act> acts, PrintStream out)  | 
|---|
 | 153 | +	{		  | 
|---|
 | 154 | +		// Write headers		  | 
|---|
 | 155 | +		List<String> names=new ArrayList<String>();  | 
|---|
 | 156 | +		List<Field> fields=db.getFields();  | 
|---|
 | 157 | +		for (Field f: fields)  | 
|---|
 | 158 | +			names.add(f.getName());  | 
|---|
 | 159 | +		print(names, out);  | 
|---|
 | 160 | +		  | 
|---|
 | 161 | +		// Write data  | 
|---|
 | 162 | +		for (Act a: acts)  | 
|---|
 | 163 | +		{  | 
|---|
 | 164 | +			List<String> data=new ArrayList<String>();  | 
|---|
 | 165 | +			for (Field f: fields)  | 
|---|
 | 166 | +				data.add(format(a.get(f)));  | 
|---|
 | 167 | +			print(data, out);  | 
|---|
 | 168 | +		}  | 
|---|
 | 169 | +	}  | 
|---|
 | 170 | +  | 
|---|
 | 171 | +	  | 
|---|
 | 172 | +	static String format(Object o)  | 
|---|
 | 173 | +	{  | 
|---|
 | 174 | +		if (o==null)  | 
|---|
 | 175 | +			return "";  | 
|---|
 | 176 | +		if (o instanceof String)  | 
|---|
 | 177 | +			return (String)o;  | 
|---|
 | 178 | +		if (o instanceof RoughTime)  | 
|---|
 | 179 | +			return ((RoughTime)o).format();  | 
|---|
 | 180 | +		if (o instanceof Number)  | 
|---|
 | 181 | +			return o.toString();  | 
|---|
 | 182 | +		if (o instanceof String[])  | 
|---|
 | 183 | +		{  | 
|---|
 | 184 | +			return writeArray((String[])o);  | 
|---|
 | 185 | +		}  | 
|---|
 | 186 | +		return o.toString();  | 
|---|
 | 187 | +	}  | 
|---|
 | 188 | +	  | 
|---|
 | 189 | +	public static String writeArray(Object[] s)  | 
|---|
 | 190 | +	{  | 
|---|
 | 191 | +        if (s==null || s.length==0)  | 
|---|
 | 192 | +        {  | 
|---|
 | 193 | +        	return "";  | 
|---|
 | 194 | +        }  | 
|---|
 | 195 | +        StringBuffer b=new StringBuffer();  | 
|---|
 | 196 | +        for (int i=0; i<s.length; i++)  | 
|---|
 | 197 | +        {  | 
|---|
 | 198 | +        	if (i>0)  | 
|---|
 | 199 | +        		b.append(",");  | 
|---|
 | 200 | +        	b.append(s[i]);  | 
|---|
 | 201 | +        	  | 
|---|
 | 202 | +        }  | 
|---|
 | 203 | +        return b.toString();  | 
|---|
 | 204 | +	}  | 
|---|
 | 205 | +	  | 
|---|
 | 206 | +	void print(List<String> list, PrintStream out)  | 
|---|
 | 207 | +	{  | 
|---|
 | 208 | +		out.println(delimitedText.write((String[])list.toArray(new String[0])));  | 
|---|
 | 209 | +	}  | 
|---|
 | 210 | +	  | 
|---|
 | 211 | +	void print(List<String> list, PrintWriter out)  | 
|---|
 | 212 | +	{  | 
|---|
 | 213 | +		out.println(delimitedText.write((String[])list.toArray(new String[0])));  | 
|---|
 | 214 | +	}  | 
|---|
 | 215 | +  | 
|---|
 | 216 | +}  | 
|---|
 | 217 | +  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.format.file;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import java.util.*;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +import timeflow.util.*;  | 
|---|
 | 6 | +  | 
|---|
 | 7 | +import timeflow.model.Display;  | 
|---|
 | 8 | +  | 
|---|
 | 9 | +public class DelimitedText {  | 
|---|
 | 10 | +	private char delimiter;  | 
|---|
 | 11 | +	  | 
|---|
 | 12 | +	public DelimitedText(char delimiter)  | 
|---|
 | 13 | +	{  | 
|---|
 | 14 | +		if (delimiter=='"')  | 
|---|
 | 15 | +			throw new IllegalArgumentException("Can't use quote as delimiter.");  | 
|---|
 | 16 | +		this.delimiter=delimiter;  | 
|---|
 | 17 | +	}  | 
|---|
 | 18 | +	  | 
|---|
 | 19 | +	private static boolean isBreak(char c)  | 
|---|
 | 20 | +	{  | 
|---|
 | 21 | +		return c=='\n' || c=='\r';  | 
|---|
 | 22 | +	}  | 
|---|
 | 23 | +	  | 
|---|
 | 24 | +	public List<String[]> read(String text)  | 
|---|
 | 25 | +	{  | 
|---|
 | 26 | +		ArrayList<String[]> results=new ArrayList<String[]>();  | 
|---|
 | 27 | +		int n=text.length();  | 
|---|
 | 28 | +		StringBuffer currentToken=new StringBuffer();  | 
|---|
 | 29 | +		ArrayList<String> currentList=new ArrayList<String>();  | 
|---|
 | 30 | +		  | 
|---|
 | 31 | +		boolean quoted=false;  | 
|---|
 | 32 | +		for (int i=0; i<n; i++)  | 
|---|
 | 33 | +		{  | 
|---|
 | 34 | +			char c=text.charAt(i);  | 
|---|
 | 35 | +			if (quoted)  | 
|---|
 | 36 | +			{  | 
|---|
 | 37 | +				if (c=='"')  | 
|---|
 | 38 | +				{  | 
|---|
 | 39 | +					if (i==n-1) // end of file, ignore quote.  | 
|---|
 | 40 | +					{  | 
|---|
 | 41 | +						quoted=false;  | 
|---|
 | 42 | +						continue;  | 
|---|
 | 43 | +					}  | 
|---|
 | 44 | +					char next=text.charAt(i+1);  | 
|---|
 | 45 | +					if (next=='"') // a quoted quote.  | 
|---|
 | 46 | +					{  | 
|---|
 | 47 | +						currentToken.append('"');  | 
|---|
 | 48 | +						i++;  | 
|---|
 | 49 | +						  | 
|---|
 | 50 | +						// Alas, there is a weird special case here  | 
|---|
 | 51 | +						// if the user has pasted from Excel.  | 
|---|
 | 52 | +						// If a field starts with a quote, and ends with two quotes,  | 
|---|
 | 53 | +						// it turns out to be ambiguous!  | 
|---|
 | 54 | +						// Excel doesn't do any escaping on: "blah blah""  | 
|---|
 | 55 | +						// But, it does escape: blah "\n  | 
|---|
 | 56 | +						// turning it into: "blah blah""\n  | 
|---|
 | 57 | +						// So if "blah blah"" occurs at the end of the line,  | 
|---|
 | 58 | +						// you actually do not know which it is!  | 
|---|
 | 59 | +						// In practice, our first bug report was for a literal of "blah blah""  | 
|---|
 | 60 | +						// so that is what we will choose.  | 
|---|
 | 61 | +						  | 
|---|
 | 62 | +						//System.out.println("next++:  '"+text.charAt(i+1)+"'="+(int)text.charAt(i+1));  | 
|---|
 | 63 | +						if (i<n-1 && isBreak(text.charAt(i+1)))  | 
|---|
 | 64 | +						{  | 
|---|
 | 65 | +							quoted=false;  | 
|---|
 | 66 | +						}  | 
|---|
 | 67 | +						  | 
|---|
 | 68 | +						continue;  | 
|---|
 | 69 | +					}  | 
|---|
 | 70 | +					if (isBreak(next)) // end of line  | 
|---|
 | 71 | +					{  | 
|---|
 | 72 | +						quoted=false;  | 
|---|
 | 73 | +						currentList.add(currentToken.toString());  | 
|---|
 | 74 | +						currentToken.setLength(0);  | 
|---|
 | 75 | +						results.add((String[])currentList.toArray(new String[0]));  | 
|---|
 | 76 | +						currentList=new ArrayList<String>();  | 
|---|
 | 77 | +						i++;  | 
|---|
 | 78 | +						if (i<n-1 && isBreak(text.charAt(i+1)))  | 
|---|
 | 79 | +							i++;  | 
|---|
 | 80 | +						continue;  | 
|---|
 | 81 | +					}  | 
|---|
 | 82 | +					if (next==delimiter)  | 
|---|
 | 83 | +					{  | 
|---|
 | 84 | +						quoted=false;  | 
|---|
 | 85 | +						continue;  | 
|---|
 | 86 | +					}  | 
|---|
 | 87 | +					System.out.println("a bad quote from excel: next char="+(int)next);  | 
|---|
 | 88 | +					quoted=false;  | 
|---|
 | 89 | +				}  | 
|---|
 | 90 | +				currentToken.append(c);  | 
|---|
 | 91 | +				continue;  | 
|---|
 | 92 | +			}  | 
|---|
 | 93 | +			  | 
|---|
 | 94 | +			// ok, not quoted.  | 
|---|
 | 95 | +			if (c==delimiter)  | 
|---|
 | 96 | +			{  | 
|---|
 | 97 | +				currentList.add(currentToken.toString());  | 
|---|
 | 98 | +				currentToken.setLength(0);  | 
|---|
 | 99 | +				quoted=false;  | 
|---|
 | 100 | +				continue;  | 
|---|
 | 101 | +			}  | 
|---|
 | 102 | +			  | 
|---|
 | 103 | +			// not delimiter, not in the middle of a quote.  | 
|---|
 | 104 | +			if (c=='"')  | 
|---|
 | 105 | +			{  | 
|---|
 | 106 | +				if (currentToken.length()==0) // we are at beginning of a token, so this is a quote.  | 
|---|
 | 107 | +				{  | 
|---|
 | 108 | +					quoted=true;  | 
|---|
 | 109 | +					continue;  | 
|---|
 | 110 | +				}  | 
|---|
 | 111 | +			}  | 
|---|
 | 112 | +						  | 
|---|
 | 113 | +			// is it a line feed? we're not in the middle of a quote, so this means a new line.  | 
|---|
 | 114 | +			if (c=='\n' || c=='\r' || c=='\f')  | 
|---|
 | 115 | +			{  | 
|---|
 | 116 | +				currentList.add(currentToken.toString());  | 
|---|
 | 117 | +				currentToken.setLength(0);  | 
|---|
 | 118 | +				results.add((String[])currentList.toArray(new String[0]));  | 
|---|
 | 119 | +				currentList=new ArrayList<String>();  | 
|---|
 | 120 | +				if (i<n-1 && (text.charAt(i+1)=='\n' || text.charAt(i+1)=='\r'))  | 
|---|
 | 121 | +					i++;  | 
|---|
 | 122 | +				continue;  | 
|---|
 | 123 | +			}  | 
|---|
 | 124 | +			  | 
|---|
 | 125 | +			// by golly, just a normal character!  | 
|---|
 | 126 | +			currentToken.append(c);  | 
|---|
 | 127 | +		}  | 
|---|
 | 128 | +		  | 
|---|
 | 129 | +		// did it just end in a blank line?  | 
|---|
 | 130 | +		  | 
|---|
 | 131 | +		if (currentList.size()>0 || currentToken.toString().trim().length()>0)  | 
|---|
 | 132 | +		{  | 
|---|
 | 133 | +			currentList.add(currentToken.toString());  | 
|---|
 | 134 | +			results.add((String[])currentList.toArray(new String[0]));	  | 
|---|
 | 135 | +		}  | 
|---|
 | 136 | +		return results;  | 
|---|
 | 137 | +	}  | 
|---|
 | 138 | +	  | 
|---|
 | 139 | +	public String write(String s)  | 
|---|
 | 140 | +	{  | 
|---|
 | 141 | +		return write(new String[] {s});  | 
|---|
 | 142 | +	}  | 
|---|
 | 143 | +	  | 
|---|
 | 144 | +	public String write(String[] data)  | 
|---|
 | 145 | +	{  | 
|---|
 | 146 | +		StringBuffer b=new StringBuffer();  | 
|---|
 | 147 | +		for (int i=0; i<data.length; i++)  | 
|---|
 | 148 | +		{  | 
|---|
 | 149 | +			// add a delimiter if necessary.  | 
|---|
 | 150 | +			if (i>0)  | 
|---|
 | 151 | +				b.append(delimiter);  | 
|---|
 | 152 | +			  | 
|---|
 | 153 | +			// if null, just don't write anything.  | 
|---|
 | 154 | +			if (data[i]==null)  | 
|---|
 | 155 | +				continue;  | 
|---|
 | 156 | +			  | 
|---|
 | 157 | +			// does it have weird characters in it?  | 
|---|
 | 158 | +			boolean weird=false;  | 
|---|
 | 159 | +			int n=data[i].length();  | 
|---|
 | 160 | +			for (int j=0; j<n; j++)  | 
|---|
 | 161 | +			{  | 
|---|
 | 162 | +				char c=data[i].charAt(j);  | 
|---|
 | 163 | +				if (c==delimiter || isBreak(c))  | 
|---|
 | 164 | +				{  | 
|---|
 | 165 | +					weird=true;  | 
|---|
 | 166 | +					break;  | 
|---|
 | 167 | +				}  | 
|---|
 | 168 | +			}  | 
|---|
 | 169 | +			  | 
|---|
 | 170 | +			if (weird)  | 
|---|
 | 171 | +			{  | 
|---|
 | 172 | +				b.append('"');  | 
|---|
 | 173 | +				for (int j=0; j<n; j++)  | 
|---|
 | 174 | +				{  | 
|---|
 | 175 | +					char c=data[i].charAt(j);  | 
|---|
 | 176 | +					if (c=='"')  | 
|---|
 | 177 | +						b.append('"');  | 
|---|
 | 178 | +					b.append(c);  | 
|---|
 | 179 | +				}  | 
|---|
 | 180 | +				b.append('"');  | 
|---|
 | 181 | +			}  | 
|---|
 | 182 | +			else  | 
|---|
 | 183 | +				b.append(data[i]);  | 
|---|
 | 184 | +		}  | 
|---|
 | 185 | +		return b.toString();  | 
|---|
 | 186 | +	}  | 
|---|
 | 187 | +	  | 
|---|
 | 188 | +	public static String[] split(String s, char delimiter)  | 
|---|
 | 189 | +	{  | 
|---|
 | 190 | +		DelimitedText t= new DelimitedText(delimiter);  | 
|---|
 | 191 | +		List<String[]> lines=t.read(s);  | 
|---|
 | 192 | +		return lines.get(0);  | 
|---|
 | 193 | +	}  | 
|---|
 | 194 | +	  | 
|---|
 | 195 | +	public static void main(String[] args) throws Exception  | 
|---|
 | 196 | +	{  | 
|---|
 | 197 | +		String bad=IO.read("test/bad-all.txt");  | 
|---|
 | 198 | +		String[][] s=DelimitedFormat.readArrayFromString(bad, System.out);  | 
|---|
 | 199 | +		System.out.println("len="+s.length);  | 
|---|
 | 200 | +  | 
|---|
 | 201 | +		/*  | 
|---|
 | 202 | +		//DelimitedText c=new DelimitedText(';');  | 
|---|
 | 203 | +		//List<String[]> arrays=c.read(IO.read("test/bad.txt"));  | 
|---|
 | 204 | +		//List<String[]> arrays=c.read("a;b;\"x;y\";c");  | 
|---|
 | 205 | +		//List<String[]> arrays=c.read("a;\"a\n\rq\";b;\"x;y\";c");  | 
|---|
 | 206 | +		//List<String[]> arrays=c.read("a;b;\"with a \"\"blah\";c\nd;e;f\ng;h;i");  | 
|---|
 | 207 | +		//List<String[]> arrays=c.read("a,\"b\",\"c\r\nd\"\r\ne,f,g\nh,i,j");  | 
|---|
 | 208 | +		for (String[] s:arrays)  | 
|---|
 | 209 | +		{  | 
|---|
 | 210 | +			System.out.println("["+Display.arrayToString(s)+"]");  | 
|---|
 | 211 | +		}  | 
|---|
 | 212 | +		*/  | 
|---|
 | 213 | +	}  | 
|---|
 | 214 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.format.file;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.model.*;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +import java.io.BufferedWriter;  | 
|---|
 | 6 | +  | 
|---|
 | 7 | +public interface Export {  | 
|---|
 | 8 | +	public String getName();  | 
|---|
 | 9 | +	public void export(TFModel model, BufferedWriter out) throws Exception;  | 
|---|
 | 10 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.format.file;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +// This is meant to be a repository for different  | 
|---|
 | 4 | +// types of import functions, arranged by file extension.  | 
|---|
 | 5 | +  | 
|---|
 | 6 | +// We currently do not import anything but the standard file type.  | 
|---|
 | 7 | +// There actually is some code that will import from JSON/XML SIMILE  | 
|---|
 | 8 | +// timelines, but we have removed it from this release to simplify  | 
|---|
 | 9 | +// both the application and because it would mean redistributing additional  | 
|---|
 | 10 | +// third-party libraries.  | 
|---|
 | 11 | +public class FileExtensionCatalog {  | 
|---|
 | 12 | +  | 
|---|
 | 13 | +	public static Import get(String fileName)  | 
|---|
 | 14 | +	{  | 
|---|
 | 15 | +		/*  | 
|---|
 | 16 | +		// not in this release...  | 
|---|
 | 17 | +		// but contact us if you'd like to see this.  | 
|---|
 | 18 | +		// we took out the SIMILE import material as too "techie"  | 
|---|
 | 19 | +		// for the first release!  | 
|---|
 | 20 | +		   | 
|---|
 | 21 | +		if (fileName.endsWith("xml"))  | 
|---|
 | 22 | +			return new SimileXMLFormat();  | 
|---|
 | 23 | +		if (fileName.endsWith("json"))  | 
|---|
 | 24 | +			return new SimileJSONFormat();  | 
|---|
 | 25 | +			*/  | 
|---|
 | 26 | +		return new TimeflowFormat();  | 
|---|
 | 27 | +	}  | 
|---|
 | 28 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.format.file;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import java.awt.Color;  | 
|---|
 | 4 | +import java.io.BufferedWriter;  | 
|---|
 | 5 | +import java.net.URL;  | 
|---|
 | 6 | +  | 
|---|
 | 7 | +import timeflow.model.*;  | 
|---|
 | 8 | +import timeflow.data.db.*;  | 
|---|
 | 9 | +  | 
|---|
 | 10 | +public class HtmlFormat implements Export  | 
|---|
 | 11 | +{  | 
|---|
 | 12 | +	TFModel model;  | 
|---|
 | 13 | +	java.util.List<Field> fields;  | 
|---|
 | 14 | +	Field title;  | 
|---|
 | 15 | +	  | 
|---|
 | 16 | +	public HtmlFormat() {}  | 
|---|
 | 17 | +	  | 
|---|
 | 18 | +	public HtmlFormat(TFModel model)  | 
|---|
 | 19 | +	{  | 
|---|
 | 20 | +		setModel(model);  | 
|---|
 | 21 | +	}  | 
|---|
 | 22 | +	  | 
|---|
 | 23 | +	public void setModel(TFModel model)  | 
|---|
 | 24 | +	{  | 
|---|
 | 25 | +		this.model=model;  | 
|---|
 | 26 | +		fields=model.getDB().getFields();  | 
|---|
 | 27 | +		title=model.getDB().getField(VirtualField.LABEL);  | 
|---|
 | 28 | +	}  | 
|---|
 | 29 | +	  | 
|---|
 | 30 | +	@Override  | 
|---|
 | 31 | +	public void export(TFModel model, BufferedWriter out) throws Exception {  | 
|---|
 | 32 | +		setModel(model);  | 
|---|
 | 33 | +		out.write(makeHeader());  | 
|---|
 | 34 | +		for (Act a: model.getDB())  | 
|---|
 | 35 | +			out.write(makeItem(a));  | 
|---|
 | 36 | +		out.write(makeFooter());  | 
|---|
 | 37 | +		out.flush();  | 
|---|
 | 38 | +	}  | 
|---|
 | 39 | +	  | 
|---|
 | 40 | +	public void append(ActList acts, int start, int end, StringBuffer b)  | 
|---|
 | 41 | +	{  | 
|---|
 | 42 | +		for (int i=start; i<end; i++)  | 
|---|
 | 43 | +		{  | 
|---|
 | 44 | +			Act a=acts.get(i);  | 
|---|
 | 45 | +			b.append(makeItem(a,i));  | 
|---|
 | 46 | +		}  | 
|---|
 | 47 | +	}  | 
|---|
 | 48 | +	  | 
|---|
 | 49 | +	private String makeItem(Act act)  | 
|---|
 | 50 | +	{  | 
|---|
 | 51 | +		return makeItem(act, -1);  | 
|---|
 | 52 | +	}  | 
|---|
 | 53 | +	  | 
|---|
 | 54 | +	public String makeItem(Act act, int id)  | 
|---|
 | 55 | +	{  | 
|---|
 | 56 | +		StringBuffer page=new StringBuffer();  | 
|---|
 | 57 | +		  | 
|---|
 | 58 | +		  | 
|---|
 | 59 | +		page.append("<tr><td valign=top align=left width=200><b>");  | 
|---|
 | 60 | +		if (title!=null)  | 
|---|
 | 61 | +		{  | 
|---|
 | 62 | +			Field f=model.getColorField();  | 
|---|
 | 63 | +			Color c=Color.black;  | 
|---|
 | 64 | +			if (f!=null)  | 
|---|
 | 65 | +			{  | 
|---|
 | 66 | +				if (f.getType()==String.class)  | 
|---|
 | 67 | +					c=model.getDisplay().makeColor(act.getString(f));  | 
|---|
 | 68 | +				else  | 
|---|
 | 69 | +				{  | 
|---|
 | 70 | +					String[] tags=act.getTextList(f);  | 
|---|
 | 71 | +					if (tags.length==0)  | 
|---|
 | 72 | +						c=Color.gray;  | 
|---|
 | 73 | +					else  | 
|---|
 | 74 | +						c=model.getDisplay().makeColor(tags[0]);  | 
|---|
 | 75 | +				}  | 
|---|
 | 76 | +			}  | 
|---|
 | 77 | +			  | 
|---|
 | 78 | +			page.append("<font size=+1 color="+htmlColor(c)+">"+act.getString(title)+"</font><br>");  | 
|---|
 | 79 | +		}  | 
|---|
 | 80 | +		  | 
|---|
 | 81 | +		Field startField=model.getDB().getField(VirtualField.START);  | 
|---|
 | 82 | +		  | 
|---|
 | 83 | +		if (startField!=null)  | 
|---|
 | 84 | +		{  | 
|---|
 | 85 | +			page.append("<font color=#999999>"+model.getDisplay().format(  | 
|---|
 | 86 | +				act.getTime(startField))+"</font>");  | 
|---|
 | 87 | +		}  | 
|---|
 | 88 | +		page.append("</b><br>");  | 
|---|
 | 89 | +		if (id>=0)  | 
|---|
 | 90 | +			page.append("<a href=\"e"+id+"\">EDIT</a>");  | 
|---|
 | 91 | +		page.append("<br></td><td valign=top>");  | 
|---|
 | 92 | +		for (Field f: fields)  | 
|---|
 | 93 | +		{  | 
|---|
 | 94 | +			page.append("<b><font color=#003399>"+f.getName()+"</font></b>  ");  | 
|---|
 | 95 | +			Object val=act.get(f);  | 
|---|
 | 96 | +			if (val instanceof URL)  | 
|---|
 | 97 | +			{  | 
|---|
 | 98 | +				page.append("<a href=\""+val+"\">"+val+"</a>");  | 
|---|
 | 99 | +			}  | 
|---|
 | 100 | +			else  | 
|---|
 | 101 | +				page.append(model.getDisplay().toString(val));  | 
|---|
 | 102 | +			page.append("<br>");  | 
|---|
 | 103 | +  | 
|---|
 | 104 | +		}  | 
|---|
 | 105 | +		page.append("<br></td></tr>");  | 
|---|
 | 106 | +  | 
|---|
 | 107 | +		return page.toString();  | 
|---|
 | 108 | +	}  | 
|---|
 | 109 | +	  | 
|---|
 | 110 | +	public String makeHeader()  | 
|---|
 | 111 | +	{  | 
|---|
 | 112 | +		StringBuffer page=new StringBuffer();  | 
|---|
 | 113 | +		page.append("<html><body><blockquote>");  | 
|---|
 | 114 | +		page.append("<br>File: "+model.getDbFile()+"<br>");  | 
|---|
 | 115 | +		page.append("Source: "+model.getDB().getSource()+"<br><br>");  | 
|---|
 | 116 | +		page.append("<br><br>");  | 
|---|
 | 117 | +		page.append("<table border=0>");  | 
|---|
 | 118 | +  | 
|---|
 | 119 | +		return page.toString();  | 
|---|
 | 120 | +	}  | 
|---|
 | 121 | +	  | 
|---|
 | 122 | +	public String makeFooter()  | 
|---|
 | 123 | +	{  | 
|---|
 | 124 | +		return "</table></blockquote></body></html>";  | 
|---|
 | 125 | +	}  | 
|---|
 | 126 | +	  | 
|---|
 | 127 | +	  | 
|---|
 | 128 | +	static String htmlColor(Color c)  | 
|---|
 | 129 | +	{  | 
|---|
 | 130 | +		return '#'+hex2(c.getRed())+hex2(c.getGreen())+hex2(c.getBlue());  | 
|---|
 | 131 | +	}  | 
|---|
 | 132 | +	  | 
|---|
 | 133 | +	private static final String hexDigits="0123456789ABCDEF";  | 
|---|
 | 134 | +	private static String hex2(int n)  | 
|---|
 | 135 | +	{  | 
|---|
 | 136 | +		return hexDigits.charAt((n/16)%16)+""+hexDigits.charAt(n%16);  | 
|---|
 | 137 | +	}  | 
|---|
 | 138 | +	@Override  | 
|---|
 | 139 | +	public String getName() {  | 
|---|
 | 140 | +		return "HTML List";  | 
|---|
 | 141 | +	}  | 
|---|
 | 142 | +  | 
|---|
 | 143 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.format.file;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import java.io.File;  | 
|---|
 | 4 | +import timeflow.data.db.ActDB;  | 
|---|
 | 5 | +  | 
|---|
 | 6 | +public interface Import {  | 
|---|
 | 7 | +	public String getName();  | 
|---|
 | 8 | +	public ActDB importFile(File file) throws Exception;  | 
|---|
 | 9 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.format.file;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.model.*;  | 
|---|
 | 4 | +import timeflow.data.db.*;  | 
|---|
 | 5 | +import timeflow.data.time.*;  | 
|---|
 | 6 | +import timeflow.format.field.*;  | 
|---|
 | 7 | +  | 
|---|
 | 8 | +import timeflow.util.*;  | 
|---|
 | 9 | +  | 
|---|
 | 10 | +import java.io.*;  | 
|---|
 | 11 | +import java.net.URL;  | 
|---|
 | 12 | +import java.util.*;  | 
|---|
 | 13 | +  | 
|---|
 | 14 | +public class TimeflowFormat implements Import, Export  | 
|---|
 | 15 | +{  | 
|---|
 | 16 | +	private static final String END_OF_SCHEMA="#TIMEFLOW\tend-metadata";  | 
|---|
 | 17 | +	private static final String END_OF_METADATA="#TIMEFLOW\t====== End of Header. Data below is in tab-delimited format. =====";  | 
|---|
 | 18 | +	public ActDB readFile(String fileName, PrintStream messages) throws Exception  | 
|---|
 | 19 | +	{  | 
|---|
 | 20 | +		return read(new File(fileName), messages);  | 
|---|
 | 21 | +	}  | 
|---|
 | 22 | +	  | 
|---|
 | 23 | +	public static ActDB read(File file, PrintStream out) throws Exception  | 
|---|
 | 24 | +	{  | 
|---|
 | 25 | +		String text=IO.read(file.getAbsolutePath());		  | 
|---|
 | 26 | +		DelimitedText quote=new DelimitedText('\t');  | 
|---|
 | 27 | +		Iterator<String[]> lines=quote.read(text).iterator();  | 
|---|
 | 28 | +		  | 
|---|
 | 29 | +  | 
|---|
 | 30 | +		ActDB db=null;  | 
|---|
 | 31 | +		List<String> fieldNames=new ArrayList<String>();  | 
|---|
 | 32 | +		List<Class> fieldTypes=new ArrayList<Class>();  | 
|---|
 | 33 | +		List<Integer> fieldSizes=new ArrayList<Integer>();  | 
|---|
 | 34 | +		String source="[unknown]", description="";  | 
|---|
 | 35 | +		for (;;)  | 
|---|
 | 36 | +		{  | 
|---|
 | 37 | +			String[] t=lines.next();  | 
|---|
 | 38 | +  | 
|---|
 | 39 | +			if (t[1].equals("field"))  | 
|---|
 | 40 | +			{  | 
|---|
 | 41 | +				fieldNames.add(t[2]);  | 
|---|
 | 42 | +				fieldTypes.add(FieldFormatCatalog.javaClass(t[3]));  | 
|---|
 | 43 | +				if (t.length>4)  | 
|---|
 | 44 | +				{  | 
|---|
 | 45 | +					fieldSizes.add(Integer.parseInt(t[4]));  | 
|---|
 | 46 | +				}  | 
|---|
 | 47 | +				else  | 
|---|
 | 48 | +					fieldSizes.add(-1);  | 
|---|
 | 49 | +			}  | 
|---|
 | 50 | +			else if (t[1].equals("source"))  | 
|---|
 | 51 | +			{  | 
|---|
 | 52 | +				source=t[2];  | 
|---|
 | 53 | +			}  | 
|---|
 | 54 | +			else if (t[1].equals("description"))  | 
|---|
 | 55 | +			{  | 
|---|
 | 56 | +				description=t[2];  | 
|---|
 | 57 | +				  | 
|---|
 | 58 | +			}  | 
|---|
 | 59 | +			else if (t[1].equals("end-metadata"))  | 
|---|
 | 60 | +				break;  | 
|---|
 | 61 | +		}  | 
|---|
 | 62 | +		db=new ArrayDB((String[])fieldNames.toArray(new String[0]),   | 
|---|
 | 63 | +				         (Class[])fieldTypes.toArray(new Class[0]), source);  | 
|---|
 | 64 | +		db.setDescription(description);  | 
|---|
 | 65 | +		for (int i=0; i<fieldNames.size(); i++)  | 
|---|
 | 66 | +			if (fieldSizes.get(i)>0)  | 
|---|
 | 67 | +				db.getField(fieldNames.get(i)).setRecommendedSize(fieldSizes.get(i));  | 
|---|
 | 68 | +		for (;;)  | 
|---|
 | 69 | +		{  | 
|---|
 | 70 | +			String[] t=lines.next();  | 
|---|
 | 71 | +			if (t[1].startsWith("==="))  | 
|---|
 | 72 | +				break;		  | 
|---|
 | 73 | +			if (t[1].equals("alias"))  | 
|---|
 | 74 | +				db.setAlias(db.getField(t[3]), t[2]);  | 
|---|
 | 75 | +		}  | 
|---|
 | 76 | +		  | 
|---|
 | 77 | +		// note: in some cases headers may be in a different order than in  | 
|---|
 | 78 | +		// metadata section, so we will read these.  | 
|---|
 | 79 | +		String[] headers=lines.next();  | 
|---|
 | 80 | +		if (headers.length!=fieldNames.size())  | 
|---|
 | 81 | +			throw new IllegalArgumentException("Different number of headers than fields!");  | 
|---|
 | 82 | +		  | 
|---|
 | 83 | +		  | 
|---|
 | 84 | +		while (lines.hasNext())  | 
|---|
 | 85 | +		{  | 
|---|
 | 86 | +			String[] t=lines.next();  | 
|---|
 | 87 | +			Act a=db.createAct();  | 
|---|
 | 88 | +			for (int i=0; i<t.length; i++)  | 
|---|
 | 89 | +			{  | 
|---|
 | 90 | +				Field f=db.getField(headers[i]);  | 
|---|
 | 91 | +				FieldFormat format=FieldFormatCatalog.getFormat(f.getType());  | 
|---|
 | 92 | +				a.set(f, format.parse(t[i]));  | 
|---|
 | 93 | +			}  | 
|---|
 | 94 | +		}  | 
|---|
 | 95 | +		  | 
|---|
 | 96 | +		return db;  | 
|---|
 | 97 | +	}  | 
|---|
 | 98 | +	  | 
|---|
 | 99 | +	public static void write(ActList acts, BufferedWriter bw) throws IOException  | 
|---|
 | 100 | +	{  | 
|---|
 | 101 | +		ActDB db=acts.getDB();  | 
|---|
 | 102 | +		  | 
|---|
 | 103 | +		PrintWriter out=new PrintWriter(bw);  | 
|---|
 | 104 | +		  | 
|---|
 | 105 | +		DelimitedText tab=new DelimitedText('\t');  | 
|---|
 | 106 | +		  | 
|---|
 | 107 | +		// Write version  | 
|---|
 | 108 | +		out.println("#TIMEFLOW\tformat version\t1");  | 
|---|
 | 109 | +		  | 
|---|
 | 110 | +		// Write source of data.  | 
|---|
 | 111 | +		out.println("#TIMEFLOW\tsource\t"+tab.write(db.getSource()));  | 
|---|
 | 112 | +		  | 
|---|
 | 113 | +		// Write description of data.  | 
|---|
 | 114 | +		out.println("#TIMEFLOW\tdescription\t"+tab.write(db.getDescription()));  | 
|---|
 | 115 | +		  | 
|---|
 | 116 | +		// Write schema.  | 
|---|
 | 117 | +		List<Field> fields=db.getFields();  | 
|---|
 | 118 | +		for (Field f: fields)  | 
|---|
 | 119 | +		{  | 
|---|
 | 120 | +			String recSize=f.getRecommendedSize()<=0 ? "" : "\t"+f.getRecommendedSize();  | 
|---|
 | 121 | +			out.println("#TIMEFLOW\tfield\t"+tab.write(f.getName())+  | 
|---|
 | 122 | +					    "\t"+FieldFormatCatalog.humanName(f.getType())+recSize);  | 
|---|
 | 123 | +		}  | 
|---|
 | 124 | +		  | 
|---|
 | 125 | +		out.println(END_OF_SCHEMA);  | 
|---|
 | 126 | +		  | 
|---|
 | 127 | +		// Write column mappings.  | 
|---|
 | 128 | +		List<String> aliases=DBUtils.getFieldAliases(db);  | 
|---|
 | 129 | +		for (String a:aliases)  | 
|---|
 | 130 | +			out.println("#TIMEFLOW\talias\t"+a+"\t"+tab.write(db.getField(a).getName()));  | 
|---|
 | 131 | +		  | 
|---|
 | 132 | +		// Write end of header indicator  | 
|---|
 | 133 | +		out.println(END_OF_METADATA);  | 
|---|
 | 134 | +		  | 
|---|
 | 135 | +		// Write data!  | 
|---|
 | 136 | +		new DelimitedFormat('\t').writeDelimited(db, acts, out);  | 
|---|
 | 137 | +		  | 
|---|
 | 138 | +		out.flush();  | 
|---|
 | 139 | +		out.close();  | 
|---|
 | 140 | +	}  | 
|---|
 | 141 | +	  | 
|---|
 | 142 | +	public static void main(String[] args) throws Exception  | 
|---|
 | 143 | +	{  | 
|---|
 | 144 | +		System.out.println("Reading");  | 
|---|
 | 145 | +		ActDB db=read(new File("test/monet.txt"), System.out);  | 
|---|
 | 146 | +		System.out.println("# lines: "+db.size());  | 
|---|
 | 147 | +	}  | 
|---|
 | 148 | +  | 
|---|
 | 149 | +	@Override  | 
|---|
 | 150 | +	public String getName() {  | 
|---|
 | 151 | +		return "TimeFlow Format";  | 
|---|
 | 152 | +	}  | 
|---|
 | 153 | +  | 
|---|
 | 154 | +	@Override  | 
|---|
 | 155 | +	public ActDB importFile(File file) throws Exception {  | 
|---|
 | 156 | +		return read(file, System.out);  | 
|---|
 | 157 | +	}  | 
|---|
 | 158 | +  | 
|---|
 | 159 | +	@Override  | 
|---|
 | 160 | +	public void export(TFModel model, BufferedWriter out) throws Exception {  | 
|---|
 | 161 | +		write(model.getDB().all(), out);  | 
|---|
 | 162 | +	}  | 
|---|
 | 163 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.model;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.data.db.filter.ValueFilter;  | 
|---|
 | 4 | +import timeflow.data.time.*;  | 
|---|
 | 5 | +  | 
|---|
 | 6 | +import java.awt.*;  | 
|---|
 | 7 | +import java.util.*;  | 
|---|
 | 8 | +import java.net.URI;  | 
|---|
 | 9 | +import java.text.*;  | 
|---|
 | 10 | +import javax.swing.*;  | 
|---|
 | 11 | +  | 
|---|
 | 12 | +// to do: read from a properties file!  | 
|---|
 | 13 | +public class Display  | 
|---|
 | 14 | +{  | 
|---|
 | 15 | +  | 
|---|
 | 16 | +        HashMap<String, String> strings = new HashMap<String, String>();  | 
|---|
 | 17 | +        HashMap<String, Integer> ints = new HashMap<String, Integer>();  | 
|---|
 | 18 | +        HashMap<String, Color> colors = new HashMap<String, Color>();  | 
|---|
 | 19 | +        HashMap<Class, String> classLabel = new HashMap<Class, String>();  | 
|---|
 | 20 | +        Color fallback = new Color(0, 53, 153, 128);  | 
|---|
 | 21 | +        String fontName = "Verdana";  | 
|---|
 | 22 | +        Font tinyFont = new Font(fontName, Font.BOLD, 9);  | 
|---|
 | 23 | +        Font smallFont = new Font(fontName, Font.PLAIN, 11);  | 
|---|
 | 24 | +        Font boldFont = new Font(fontName, Font.BOLD, 12);  | 
|---|
 | 25 | +        Font plainFont = UIManager.getFont("Label.font");  | 
|---|
 | 26 | +        Font bigFont = plainFont.deriveFont(Font.BOLD);  | 
|---|
 | 27 | +        Font hugeFont = new Font(fontName, Font.BOLD, 16);  | 
|---|
 | 28 | +        Font timeLabelFont = tinyFont;  | 
|---|
 | 29 | +        FontMetrics hugeFontMetrics = Toolkit.getDefaultToolkit().getFontMetrics(hugeFont);  | 
|---|
 | 30 | +        FontMetrics bigFontMetrics = Toolkit.getDefaultToolkit().getFontMetrics(bigFont);  | 
|---|
 | 31 | +        FontMetrics plainFontMetrics = Toolkit.getDefaultToolkit().getFontMetrics(plainFont);  | 
|---|
 | 32 | +        FontMetrics boldFontMetrics = Toolkit.getDefaultToolkit().getFontMetrics(boldFont);  | 
|---|
 | 33 | +        FontMetrics tinyFontMetrics = Toolkit.getDefaultToolkit().getFontMetrics(tinyFont);  | 
|---|
 | 34 | +        FontMetrics timeLabelFontMetrics = Toolkit.getDefaultToolkit().getFontMetrics(timeLabelFont);  | 
|---|
 | 35 | +        static DecimalFormat df = new DecimalFormat("###,###,###,###.##");  | 
|---|
 | 36 | +        static DecimalFormat roundFormat = new DecimalFormat("###,###,###,###");  | 
|---|
 | 37 | +        public static final String MISC_CODE = "Misc.    ";  | 
|---|
 | 38 | +        public static final double MAX_DOT_SIZE = 10;  | 
|---|
 | 39 | +        public static final int CALENDAR_CELL_HEIGHT = 80;  | 
|---|
 | 40 | +        public static Color barColor = new Color(150, 170, 200);  | 
|---|
 | 41 | +        private static final double PHI = (1 + Math.sqrt(5)) / 2;  | 
|---|
 | 42 | +        HashMap<String, Color> topColors = new HashMap<String, Color>();  | 
|---|
 | 43 | +        static Color[] handPalette =  | 
|---|
 | 44 | +        {  | 
|---|
 | 45 | +                new Color(203, 31, 23),  | 
|---|
 | 46 | +                new Color(237, 131, 0),  | 
|---|
 | 47 | +                new Color(71, 175, 13),  | 
|---|
 | 48 | +                new Color(6, 119, 207),  | 
|---|
 | 49 | +                new Color(0, 188, 184),  | 
|---|
 | 50 | +                new Color(209, 80, 174),  | 
|---|
 | 51 | +                new Color(146, 6, 0),  | 
|---|
 | 52 | +                new Color(175, 103, 0),  | 
|---|
 | 53 | +                new Color(76, 124, 0),  | 
|---|
 | 54 | +                new Color(0, 80, 143),  | 
|---|
 | 55 | +                new Color(0, 128, 124),  | 
|---|
 | 56 | +                new Color(153, 56, 126)  | 
|---|
 | 57 | +        };  | 
|---|
 | 58 | +        ValueFilter grayFilter;  | 
|---|
 | 59 | +  | 
|---|
 | 60 | +        public Display()  | 
|---|
 | 61 | +        {  | 
|---|
 | 62 | +  | 
|---|
 | 63 | +                strings.put("other.label", "Other");  | 
|---|
 | 64 | +                strings.put("null.label", "[none]");  | 
|---|
 | 65 | +  | 
|---|
 | 66 | +                classLabel.put(RoughTime.class, "Date/Time");  | 
|---|
 | 67 | +                classLabel.put(String.class, "Text");  | 
|---|
 | 68 | +                classLabel.put(Number.class, "Number");  | 
|---|
 | 69 | +                classLabel.put(Double.class, "Number");  | 
|---|
 | 70 | +                classLabel.put(Integer.class, "Number");  | 
|---|
 | 71 | +                classLabel.put(String[].class, "List");  | 
|---|
 | 72 | +  | 
|---|
 | 73 | +                colors.put("chart.background", Color.white);  | 
|---|
 | 74 | +                Color ui = new Color(240, 240, 240);  | 
|---|
 | 75 | +                colors.put("splash.background", Color.white);  | 
|---|
 | 76 | +                colors.put("splash.text", new Color(50, 50, 50));  | 
|---|
 | 77 | +                colors.put("filterpanel.background", ui);  | 
|---|
 | 78 | +                colors.put("visualpanel.background", ui);  | 
|---|
 | 79 | +                colors.put("text.prominent", Color.black);  | 
|---|
 | 80 | +                colors.put("text.normal", Color.gray);  | 
|---|
 | 81 | +                colors.put("timeline.label", new Color(0, 53, 153));  | 
|---|
 | 82 | +                colors.put("timeline.label.lesser", new Color(110, 153, 200));  | 
|---|
 | 83 | +                colors.put("timeline.grid", new Color(240, 240, 240));  | 
|---|
 | 84 | +                colors.put("timeline.grid.vertical", new Color(240, 240, 240));  | 
|---|
 | 85 | +                colors.put("timeline.zebra", new Color(245, 245, 245));  | 
|---|
 | 86 | +                colors.put("null.color", new Color(230, 230, 230));  | 
|---|
 | 87 | +                colors.put("timeline.unspecified.color", new Color(0, 53, 153));  | 
|---|
 | 88 | +                colors.put("highlight.color", new Color(0, 53, 153));  | 
|---|
 | 89 | +  | 
|---|
 | 90 | +                ints.put("timeline.datelabel.height", 20);  | 
|---|
 | 91 | +                ints.put("timeline.item.height.min", 16);  | 
|---|
 | 92 | +        }  | 
|---|
 | 93 | +  | 
|---|
 | 94 | +        public static void launchBrowser(String urlString)  | 
|---|
 | 95 | +        {  | 
|---|
 | 96 | +                if (Desktop.isDesktopSupported())  | 
|---|
 | 97 | +                {  | 
|---|
 | 98 | +                        Desktop desktop = Desktop.getDesktop();  | 
|---|
 | 99 | +                        try  | 
|---|
 | 100 | +                        {  | 
|---|
 | 101 | +                                desktop.browse(new URI(urlString));  | 
|---|
 | 102 | +                        } catch (Exception e2)  | 
|---|
 | 103 | +                        {  | 
|---|
 | 104 | +                                e2.printStackTrace();  | 
|---|
 | 105 | +                        }  | 
|---|
 | 106 | +                } else  | 
|---|
 | 107 | +                {  | 
|---|
 | 108 | +                        System.out.println("Desktop not supported!");  | 
|---|
 | 109 | +                }  | 
|---|
 | 110 | +        }  | 
|---|
 | 111 | +  | 
|---|
 | 112 | +        public static String arrayToString(Object[] s)  | 
|---|
 | 113 | +        {  | 
|---|
 | 114 | +                if (s == null || s.length == 0)  | 
|---|
 | 115 | +                {  | 
|---|
 | 116 | +                        return "";  | 
|---|
 | 117 | +                }  | 
|---|
 | 118 | +                StringBuffer b = new StringBuffer();  | 
|---|
 | 119 | +                for (int i = 0; i < s.length; i++)  | 
|---|
 | 120 | +                {  | 
|---|
 | 121 | +                        if (i > 0)  | 
|---|
 | 122 | +                        {  | 
|---|
 | 123 | +                                b.append(", ");  | 
|---|
 | 124 | +                        }  | 
|---|
 | 125 | +                        b.append(s[i]);  | 
|---|
 | 126 | +  | 
|---|
 | 127 | +                }  | 
|---|
 | 128 | +                return b.toString();  | 
|---|
 | 129 | +        }  | 
|---|
 | 130 | +  | 
|---|
 | 131 | +        public static String version()  | 
|---|
 | 132 | +        {  | 
|---|
 | 133 | +                return "TimeFlow 0.5";  | 
|---|
 | 134 | +        }  | 
|---|
 | 135 | +  | 
|---|
 | 136 | +        public Font bold()  | 
|---|
 | 137 | +        {  | 
|---|
 | 138 | +                return boldFont;  | 
|---|
 | 139 | +        }  | 
|---|
 | 140 | +  | 
|---|
 | 141 | +        public Font plain()  | 
|---|
 | 142 | +        {  | 
|---|
 | 143 | +                return plainFont;  | 
|---|
 | 144 | +        }  | 
|---|
 | 145 | +  | 
|---|
 | 146 | +        public Font small()  | 
|---|
 | 147 | +        {  | 
|---|
 | 148 | +                return smallFont;  | 
|---|
 | 149 | +        }  | 
|---|
 | 150 | +  | 
|---|
 | 151 | +        public FontMetrics hugeFontMetrics()  | 
|---|
 | 152 | +        {  | 
|---|
 | 153 | +                return hugeFontMetrics;  | 
|---|
 | 154 | +        }  | 
|---|
 | 155 | +  | 
|---|
 | 156 | +        public FontMetrics plainFontMetrics()  | 
|---|
 | 157 | +        {  | 
|---|
 | 158 | +                return plainFontMetrics;  | 
|---|
 | 159 | +        }  | 
|---|
 | 160 | +  | 
|---|
 | 161 | +        public FontMetrics boldFontMetrics()  | 
|---|
 | 162 | +        {  | 
|---|
 | 163 | +                return boldFontMetrics;  | 
|---|
 | 164 | +        }  | 
|---|
 | 165 | +  | 
|---|
 | 166 | +        public FontMetrics tinyFontMetrics()  | 
|---|
 | 167 | +        {  | 
|---|
 | 168 | +                return tinyFontMetrics;  | 
|---|
 | 169 | +        }  | 
|---|
 | 170 | +  | 
|---|
 | 171 | +        public FontMetrics timeLabelFontMetrics()  | 
|---|
 | 172 | +        {  | 
|---|
 | 173 | +                return timeLabelFontMetrics;  | 
|---|
 | 174 | +        }  | 
|---|
 | 175 | +  | 
|---|
 | 176 | +        public Font huge()  | 
|---|
 | 177 | +        {  | 
|---|
 | 178 | +                return hugeFont;  | 
|---|
 | 179 | +        }  | 
|---|
 | 180 | +  | 
|---|
 | 181 | +        public Font big()  | 
|---|
 | 182 | +        {  | 
|---|
 | 183 | +                return bigFont;  | 
|---|
 | 184 | +        }  | 
|---|
 | 185 | +  | 
|---|
 | 186 | +        public Font tiny()  | 
|---|
 | 187 | +        {  | 
|---|
 | 188 | +                return tinyFont;  | 
|---|
 | 189 | +        }  | 
|---|
 | 190 | +  | 
|---|
 | 191 | +        public Font timeLabel()  | 
|---|
 | 192 | +        {  | 
|---|
 | 193 | +                return timeLabelFont;  | 
|---|
 | 194 | +        }  | 
|---|
 | 195 | +  | 
|---|
 | 196 | +        public String getString(String s)  | 
|---|
 | 197 | +        {  | 
|---|
 | 198 | +                return strings.get(s);  | 
|---|
 | 199 | +        }  | 
|---|
 | 200 | +  | 
|---|
 | 201 | +        public int getInt(String s)  | 
|---|
 | 202 | +        {  | 
|---|
 | 203 | +                return ints.get(s);  | 
|---|
 | 204 | +        }  | 
|---|
 | 205 | +  | 
|---|
 | 206 | +        public Color getColor(String key)  | 
|---|
 | 207 | +        {  | 
|---|
 | 208 | +                if (colors.get(key) == null)  | 
|---|
 | 209 | +                {  | 
|---|
 | 210 | +                        throw new IllegalArgumentException("No color for " + key);  | 
|---|
 | 211 | +                }  | 
|---|
 | 212 | +                return colors.get(key);  | 
|---|
 | 213 | +        }  | 
|---|
 | 214 | +  | 
|---|
 | 215 | +        public JLabel label(String s)  | 
|---|
 | 216 | +        {  | 
|---|
 | 217 | +                return new JLabel(s);  | 
|---|
 | 218 | +        }  | 
|---|
 | 219 | +  | 
|---|
 | 220 | +        public String format(String s, int maxLength, boolean tryNoDots)  | 
|---|
 | 221 | +        {  | 
|---|
 | 222 | +                if (s == null)  | 
|---|
 | 223 | +                {  | 
|---|
 | 224 | +                        return "";  | 
|---|
 | 225 | +                }  | 
|---|
 | 226 | +                int n = s.length();  | 
|---|
 | 227 | +                if (n <= maxLength)  | 
|---|
 | 228 | +                {  | 
|---|
 | 229 | +                        return s;  | 
|---|
 | 230 | +                }  | 
|---|
 | 231 | +                if (maxLength < 4)  | 
|---|
 | 232 | +                {  | 
|---|
 | 233 | +                        return "...";  | 
|---|
 | 234 | +                }  | 
|---|
 | 235 | +                if (!tryNoDots)  | 
|---|
 | 236 | +                {  | 
|---|
 | 237 | +                        return s.substring(0, maxLength - 3) + "...";  | 
|---|
 | 238 | +                }  | 
|---|
 | 239 | +                // find last space before maxLength and after maxLength/2  | 
|---|
 | 240 | +                for (int j = maxLength - 1; j > maxLength / 2; j--)  | 
|---|
 | 241 | +                {  | 
|---|
 | 242 | +                        if (s.charAt(j) == ' ')  | 
|---|
 | 243 | +                        {  | 
|---|
 | 244 | +                                return s.substring(0, j);  | 
|---|
 | 245 | +                        }  | 
|---|
 | 246 | +                }  | 
|---|
 | 247 | +                return s.length() <= maxLength ? s : (maxLength < 6 ? "" : s.substring(0, maxLength - 3) + "...");  | 
|---|
 | 248 | +        }  | 
|---|
 | 249 | +  | 
|---|
 | 250 | +        public void refreshColors(Iterable<String> list)  | 
|---|
 | 251 | +        {  | 
|---|
 | 252 | +                topColors = new HashMap<String, Color>();  | 
|---|
 | 253 | +                double x = .1;  | 
|---|
 | 254 | +                int i = 0;  | 
|---|
 | 255 | +                for (String s : list)  | 
|---|
 | 256 | +                {  | 
|---|
 | 257 | +                        topColors.put(s, i < handPalette.length ? handPalette[i] : palette(x));  | 
|---|
 | 258 | +                        i++;  | 
|---|
 | 259 | +                        x += PHI;  | 
|---|
 | 260 | +                }  | 
|---|
 | 261 | +        }  | 
|---|
 | 262 | +  | 
|---|
 | 263 | +        public Color makeColor(String text)  | 
|---|
 | 264 | +        {  | 
|---|
 | 265 | +                if (grayFilter != null && !grayFilter.ok(text))  | 
|---|
 | 266 | +                {  | 
|---|
 | 267 | +                        return new Color(200, 200, 200);//"null.color");  | 
|---|
 | 268 | +                }  | 
|---|
 | 269 | +                Color c = topColors.get(text);  | 
|---|
 | 270 | +                return c == null ? _makeColor(text) : c;  | 
|---|
 | 271 | +        }  | 
|---|
 | 272 | +  | 
|---|
 | 273 | +        private Color _makeColor(String text)  | 
|---|
 | 274 | +        {  | 
|---|
 | 275 | +                if (text == null)  | 
|---|
 | 276 | +                {  | 
|---|
 | 277 | +                        return getColor("null.color");  | 
|---|
 | 278 | +                }  | 
|---|
 | 279 | +  | 
|---|
 | 280 | +                int c = Math.abs(text.hashCode());  | 
|---|
 | 281 | +                double h = ((c >> 8) % 255) / 255.;  | 
|---|
 | 282 | +                return palette(h);  | 
|---|
 | 283 | +        }  | 
|---|
 | 284 | +  | 
|---|
 | 285 | +        public static Color palette(double x)  | 
|---|
 | 286 | +        {  | 
|---|
 | 287 | +                float h = (float) (Math.abs(x) % 1);  | 
|---|
 | 288 | +                float s = .8f - .25f * h;  | 
|---|
 | 289 | +                float b = .8f - .25f * h;  | 
|---|
 | 290 | +                return new Color(Color.HSBtoRGB(h, s, b));  | 
|---|
 | 291 | +        }  | 
|---|
 | 292 | +  | 
|---|
 | 293 | +        public String getMiscLabel()  | 
|---|
 | 294 | +        {  | 
|---|
 | 295 | +                return getString("other.label");  | 
|---|
 | 296 | +        }  | 
|---|
 | 297 | +  | 
|---|
 | 298 | +        public String getNullLabel()  | 
|---|
 | 299 | +        {  | 
|---|
 | 300 | +                return getString("null.label");  | 
|---|
 | 301 | +        }  | 
|---|
 | 302 | +  | 
|---|
 | 303 | +        public String toString(Object o)  | 
|---|
 | 304 | +        {  | 
|---|
 | 305 | +                if (o == null)  | 
|---|
 | 306 | +                {  | 
|---|
 | 307 | +                        return getNullLabel();  | 
|---|
 | 308 | +                }  | 
|---|
 | 309 | +                if (o instanceof Object[])  | 
|---|
 | 310 | +                {  | 
|---|
 | 311 | +                        return arrayToString((Object[]) o);  | 
|---|
 | 312 | +                }  | 
|---|
 | 313 | +                if (o instanceof RoughTime)  | 
|---|
 | 314 | +                {  | 
|---|
 | 315 | +                        return ((RoughTime) o).format();//UnitOfTime.format((RoughTime)o);  | 
|---|
 | 316 | +                }  | 
|---|
 | 317 | +                if (o instanceof Double)  | 
|---|
 | 318 | +                {  | 
|---|
 | 319 | +                        return df.format((Double) o);  | 
|---|
 | 320 | +                }  | 
|---|
 | 321 | +                return o.toString();  | 
|---|
 | 322 | +        }  | 
|---|
 | 323 | +  | 
|---|
 | 324 | +        public static String format(double x)  | 
|---|
 | 325 | +        {  | 
|---|
 | 326 | +                return Math.abs(x) > 999 ? roundFormat.format(x) : df.format(x);  | 
|---|
 | 327 | +        }  | 
|---|
 | 328 | +  | 
|---|
 | 329 | +        public String format(RoughTime time)  | 
|---|
 | 330 | +        {  | 
|---|
 | 331 | +                if (time == null)  | 
|---|
 | 332 | +                {  | 
|---|
 | 333 | +                        return getString("null.label");  | 
|---|
 | 334 | +                }  | 
|---|
 | 335 | +                return time.format();//UnitOfTime.format(time);  | 
|---|
 | 336 | +        }  | 
|---|
 | 337 | +  | 
|---|
 | 338 | +        public static ArrayList<String> breakLines(String s, int lineChars, int firstOffset)  | 
|---|
 | 339 | +        {  | 
|---|
 | 340 | +                ArrayList<String> lines = new ArrayList<String>();  | 
|---|
 | 341 | +                String[] words = s.split(" ");  | 
|---|
 | 342 | +                String line = "";  | 
|---|
 | 343 | +  | 
|---|
 | 344 | +                for (int i = 0; i < words.length; i++)  | 
|---|
 | 345 | +                {  | 
|---|
 | 346 | +                        // is the word just too, too long?  | 
|---|
 | 347 | +                        int n = words[i].length();  | 
|---|
 | 348 | +                        if (n > lineChars - 5)  | 
|---|
 | 349 | +                        {  | 
|---|
 | 350 | +                                words[i] = words[i].substring(0, lineChars / 2 - 2) + "..." + words[i].substring(n - lineChars / 2 + 2, n);  | 
|---|
 | 351 | +                        }  | 
|---|
 | 352 | +                        if (line.length() + words[i].length() > (lines.size() == 0 ? lineChars - firstOffset : lineChars))  | 
|---|
 | 353 | +                        {  | 
|---|
 | 354 | +                                lines.add(line);  | 
|---|
 | 355 | +                                line = "";  | 
|---|
 | 356 | +                        }  | 
|---|
 | 357 | +                        line += " " + words[i];  | 
|---|
 | 358 | +                }  | 
|---|
 | 359 | +                lines.add(line);  | 
|---|
 | 360 | +                return lines;  | 
|---|
 | 361 | +        }  | 
|---|
 | 362 | +  | 
|---|
 | 363 | +        public boolean emptyMessage(Graphics g, TFModel model)  | 
|---|
 | 364 | +        {  | 
|---|
 | 365 | +                if (model.getActs() == null || model.getActs().size() == 0)  | 
|---|
 | 366 | +                {  | 
|---|
 | 367 | +                        g.setColor(getColor("text.prominent"));  | 
|---|
 | 368 | +                        g.drawString(model.getDB() == null || model.getDB().size() == 0 ? "Empty Database" : "No items found.", 10, 25);  | 
|---|
 | 369 | +                        return true;  | 
|---|
 | 370 | +                }  | 
|---|
 | 371 | +                return false;  | 
|---|
 | 372 | +        }  | 
|---|
 | 373 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.model;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import javax.swing.*;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +public abstract class ModelPanel extends JPanel implements TFListener {  | 
|---|
 | 6 | +	  | 
|---|
 | 7 | +	TFModel model;  | 
|---|
 | 8 | +	  | 
|---|
 | 9 | +	public ModelPanel(TFModel model)  | 
|---|
 | 10 | +	{  | 
|---|
 | 11 | +		this.model=model;  | 
|---|
 | 12 | +	}  | 
|---|
 | 13 | +	  | 
|---|
 | 14 | +	@Override  | 
|---|
 | 15 | +	public void addNotify()  | 
|---|
 | 16 | +	{  | 
|---|
 | 17 | +		super.addNotify();  | 
|---|
 | 18 | +		model.addListener(this);  | 
|---|
 | 19 | +	}  | 
|---|
 | 20 | +	  | 
|---|
 | 21 | +	  | 
|---|
 | 22 | +	@Override  | 
|---|
 | 23 | +	public void removeNotify()  | 
|---|
 | 24 | +	{  | 
|---|
 | 25 | +		super.removeNotify();  | 
|---|
 | 26 | +		model.removeListener(this);  | 
|---|
 | 27 | +	}  | 
|---|
 | 28 | +	  | 
|---|
 | 29 | +	public TFModel getModel()  | 
|---|
 | 30 | +	{  | 
|---|
 | 31 | +		return model;  | 
|---|
 | 32 | +	}  | 
|---|
 | 33 | +  | 
|---|
 | 34 | +	@Override  | 
|---|
 | 35 | +	public abstract void note(TFEvent e);  | 
|---|
 | 36 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.model;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +public class TFEvent {  | 
|---|
 | 4 | +	public enum Type {DATABASE_CHANGE, ACT_ADD, ACT_DELETE, ACT_CHANGE, ERROR, SOURCE_CHANGE, DESCRIPTION_CHANGE,  | 
|---|
 | 5 | +					 FIELD_ADD, FIELD_DELETE, FIELD_CHANGE, SELECTION_CHANGE, FILTER_CHANGE, VIEW_CHANGE};  | 
|---|
 | 6 | +	public Type type;  | 
|---|
 | 7 | +	public String message="[]";  | 
|---|
 | 8 | +	public Object info;  | 
|---|
 | 9 | +	public Object origin;  | 
|---|
 | 10 | +	  | 
|---|
 | 11 | +	public TFEvent(Type type, Object origin)  | 
|---|
 | 12 | +	{  | 
|---|
 | 13 | +		this.type=type;  | 
|---|
 | 14 | +		this.origin=origin;  | 
|---|
 | 15 | +	}  | 
|---|
 | 16 | +  | 
|---|
 | 17 | +	public String toString()  | 
|---|
 | 18 | +	{  | 
|---|
 | 19 | +		return "[TimelineEvent: type="+type+", info="+info+", message="+message+", origin="+origin+"]";  | 
|---|
 | 20 | +	}  | 
|---|
 | 21 | +	  | 
|---|
 | 22 | +	public boolean affectsSchema()  | 
|---|
 | 23 | +	{  | 
|---|
 | 24 | +		switch (type){  | 
|---|
 | 25 | +			case DATABASE_CHANGE:   | 
|---|
 | 26 | +			case FIELD_ADD:  | 
|---|
 | 27 | +			case FIELD_DELETE:  | 
|---|
 | 28 | +			case FIELD_CHANGE: return true;  | 
|---|
 | 29 | +		}  | 
|---|
 | 30 | +		return false;  | 
|---|
 | 31 | +	}  | 
|---|
 | 32 | +	  | 
|---|
 | 33 | +	public boolean affectsRowSet()  | 
|---|
 | 34 | +	{  | 
|---|
 | 35 | +		return affectsSchema() || type==Type.ACT_CHANGE || type== Type.ACT_ADD || type== Type.ACT_DELETE  | 
|---|
 | 36 | +		|| type==Type.FILTER_CHANGE;  | 
|---|
 | 37 | +	}  | 
|---|
 | 38 | +	  | 
|---|
 | 39 | +	public boolean affectsData()  | 
|---|
 | 40 | +	{  | 
|---|
 | 41 | +		return type!=Type.SELECTION_CHANGE && type!=Type.VIEW_CHANGE && type!=Type.ERROR;  | 
|---|
 | 42 | +	}  | 
|---|
 | 43 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.model;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +public interface TFListener {  | 
|---|
 | 4 | +	public void note(TFEvent e);  | 
|---|
 | 5 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.model;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.data.db.*;  | 
|---|
 | 4 | +import timeflow.data.db.filter.*;  | 
|---|
 | 5 | +import timeflow.data.time.*;  | 
|---|
 | 6 | +  | 
|---|
 | 7 | +  | 
|---|
 | 8 | +import java.util.*;  | 
|---|
 | 9 | +  | 
|---|
 | 10 | +// encapsulates all properties of a timeline model:  | 
|---|
 | 11 | +// data, display properties, etc.  | 
|---|
 | 12 | +// also does listening, etc.  | 
|---|
 | 13 | +public class TFModel  | 
|---|
 | 14 | +{  | 
|---|
 | 15 | +  | 
|---|
 | 16 | +        private ActDB db;  | 
|---|
 | 17 | +        private ActList acts;  | 
|---|
 | 18 | +        private ActFilter filter = new ConstFilter(true);  | 
|---|
 | 19 | +        private ArrayList<TFListener> listeners = new ArrayList<TFListener>();  | 
|---|
 | 20 | +        private Display display = new Display();  | 
|---|
 | 21 | +        private String[] labelGuesses =  | 
|---|
 | 22 | +        {  | 
|---|
 | 23 | +                "label", "LABEL", "Label", "title", "TITLE", "Title",  | 
|---|
 | 24 | +                "name", "Name", "NAME"  | 
|---|
 | 25 | +        };  | 
|---|
 | 26 | +        private String[] startGuesses =  | 
|---|
 | 27 | +        {  | 
|---|
 | 28 | +                "start", "Start", "START"  | 
|---|
 | 29 | +        };  | 
|---|
 | 30 | +        private String dbFile = "[unknown source]";  | 
|---|
 | 31 | +        private boolean changedSinceSave;  | 
|---|
 | 32 | +        private boolean readOnly;  | 
|---|
 | 33 | +        private double minSize, maxSize;  | 
|---|
 | 34 | +        private Interval viewInterval;  | 
|---|
 | 35 | +  | 
|---|
 | 36 | +        public ValueFilter getGrayFilter()  | 
|---|
 | 37 | +        {  | 
|---|
 | 38 | +                return display.grayFilter;  | 
|---|
 | 39 | +        }  | 
|---|
 | 40 | +  | 
|---|
 | 41 | +        public void setGrayFilter(ValueFilter grayFilter, Object origin)  | 
|---|
 | 42 | +        {  | 
|---|
 | 43 | +                display.grayFilter = grayFilter;  | 
|---|
 | 44 | +                fireEvent(new TFEvent(TFEvent.Type.FILTER_CHANGE, origin));  | 
|---|
 | 45 | +        }  | 
|---|
 | 46 | +  | 
|---|
 | 47 | +        public ActFilter getFilter()  | 
|---|
 | 48 | +        {  | 
|---|
 | 49 | +                return filter;  | 
|---|
 | 50 | +        }  | 
|---|
 | 51 | +  | 
|---|
 | 52 | +        public Interval getViewInterval()  | 
|---|
 | 53 | +        {  | 
|---|
 | 54 | +                return viewInterval;  | 
|---|
 | 55 | +        }  | 
|---|
 | 56 | +  | 
|---|
 | 57 | +        public void setViewInterval(Interval viewInterval)  | 
|---|
 | 58 | +        {  | 
|---|
 | 59 | +                this.viewInterval = viewInterval;  | 
|---|
 | 60 | +        }  | 
|---|
 | 61 | +  | 
|---|
 | 62 | +        public double getMinSize()  | 
|---|
 | 63 | +        {  | 
|---|
 | 64 | +                return minSize;  | 
|---|
 | 65 | +        }  | 
|---|
 | 66 | +  | 
|---|
 | 67 | +        public void setMinSize(double minSize)  | 
|---|
 | 68 | +        {  | 
|---|
 | 69 | +                this.minSize = minSize;  | 
|---|
 | 70 | +        }  | 
|---|
 | 71 | +  | 
|---|
 | 72 | +        public double getMaxSize()  | 
|---|
 | 73 | +        {  | 
|---|
 | 74 | +                return maxSize;  | 
|---|
 | 75 | +        }  | 
|---|
 | 76 | +  | 
|---|
 | 77 | +        public void setMaxSize(double maxSize)  | 
|---|
 | 78 | +        {  | 
|---|
 | 79 | +                this.maxSize = maxSize;  | 
|---|
 | 80 | +        }  | 
|---|
 | 81 | +  | 
|---|
 | 82 | +        public String getDbFile()  | 
|---|
 | 83 | +        {  | 
|---|
 | 84 | +                return dbFile;  | 
|---|
 | 85 | +        }  | 
|---|
 | 86 | +  | 
|---|
 | 87 | +        public boolean getReadOnly()  | 
|---|
 | 88 | +        {  | 
|---|
 | 89 | +                return readOnly;  | 
|---|
 | 90 | +        }  | 
|---|
 | 91 | +  | 
|---|
 | 92 | +        public void setDbFile(String dbFile, boolean readOnly, Object origin)  | 
|---|
 | 93 | +        {  | 
|---|
 | 94 | +                this.dbFile = dbFile;  | 
|---|
 | 95 | +                fireEvent(new TFEvent(TFEvent.Type.SOURCE_CHANGE, origin));  | 
|---|
 | 96 | +        }  | 
|---|
 | 97 | +  | 
|---|
 | 98 | +        public boolean isChangedSinceSave()  | 
|---|
 | 99 | +        {  | 
|---|
 | 100 | +                return changedSinceSave;  | 
|---|
 | 101 | +        }  | 
|---|
 | 102 | +  | 
|---|
 | 103 | +        public void setChangedSinceSave(boolean changedSinceSave)  | 
|---|
 | 104 | +        {  | 
|---|
 | 105 | +                this.changedSinceSave = changedSinceSave;  | 
|---|
 | 106 | +        }  | 
|---|
 | 107 | +  | 
|---|
 | 108 | +        public Display getDisplay()  | 
|---|
 | 109 | +        {  | 
|---|
 | 110 | +                return display;  | 
|---|
 | 111 | +        }  | 
|---|
 | 112 | +  | 
|---|
 | 113 | +        public ActDB getDB()  | 
|---|
 | 114 | +        {  | 
|---|
 | 115 | +                return db;  | 
|---|
 | 116 | +        }  | 
|---|
 | 117 | +  | 
|---|
 | 118 | +        public ActList getActs()  | 
|---|
 | 119 | +        {  | 
|---|
 | 120 | +                return acts;  | 
|---|
 | 121 | +        }  | 
|---|
 | 122 | +  | 
|---|
 | 123 | +        public void addListener(TFListener t)  | 
|---|
 | 124 | +        {  | 
|---|
 | 125 | +                listeners.add(t);  | 
|---|
 | 126 | +        }  | 
|---|
 | 127 | +  | 
|---|
 | 128 | +        public void removeListener(TFListener t)  | 
|---|
 | 129 | +        {  | 
|---|
 | 130 | +                listeners.remove(t);  | 
|---|
 | 131 | +        }  | 
|---|
 | 132 | +  | 
|---|
 | 133 | +        public void noteNewDescription(Object origin)  | 
|---|
 | 134 | +        {  | 
|---|
 | 135 | +                setChangedSinceSave(true);  | 
|---|
 | 136 | +                fireEvent(new TFEvent(TFEvent.Type.DESCRIPTION_CHANGE, origin));  | 
|---|
 | 137 | +        }  | 
|---|
 | 138 | +  | 
|---|
 | 139 | +        public void noteNewSource(Object origin)  | 
|---|
 | 140 | +        {  | 
|---|
 | 141 | +                setChangedSinceSave(true);  | 
|---|
 | 142 | +                fireEvent(new TFEvent(TFEvent.Type.SOURCE_CHANGE, origin));  | 
|---|
 | 143 | +        }  | 
|---|
 | 144 | +  | 
|---|
 | 145 | +        public void noteRecordChange(Object origin)  | 
|---|
 | 146 | +        {  | 
|---|
 | 147 | +                setChangedSinceSave(true);  | 
|---|
 | 148 | +                fireEvent(new TFEvent(TFEvent.Type.ACT_CHANGE, origin));  | 
|---|
 | 149 | +        }  | 
|---|
 | 150 | +  | 
|---|
 | 151 | +        public void noteAddField(Object origin)  | 
|---|
 | 152 | +        {  | 
|---|
 | 153 | +                setChangedSinceSave(true);  | 
|---|
 | 154 | +                fireEvent(new TFEvent(TFEvent.Type.FIELD_ADD, origin));  | 
|---|
 | 155 | +        }  | 
|---|
 | 156 | +  | 
|---|
 | 157 | +        public void noteError(Object origin)  | 
|---|
 | 158 | +        {  | 
|---|
 | 159 | +                fireEvent(new TFEvent(TFEvent.Type.ERROR, origin));  | 
|---|
 | 160 | +        }  | 
|---|
 | 161 | +  | 
|---|
 | 162 | +        public void noteDelete(Object origin)  | 
|---|
 | 163 | +        {  | 
|---|
 | 164 | +                setChangedSinceSave(true);  | 
|---|
 | 165 | +                updateActs();  | 
|---|
 | 166 | +                fireEvent(new TFEvent(TFEvent.Type.ACT_DELETE, origin));  | 
|---|
 | 167 | +        }  | 
|---|
 | 168 | +  | 
|---|
 | 169 | +        public void noteSchemaChange(Object origin)  | 
|---|
 | 170 | +        {  | 
|---|
 | 171 | +                setChangedSinceSave(true);  | 
|---|
 | 172 | +                updateActs();  | 
|---|
 | 173 | +                fireEvent(new TFEvent(TFEvent.Type.DATABASE_CHANGE, origin)); // @TODO: make schema change?   | 
|---|
 | 174 | +        }  | 
|---|
 | 175 | +  | 
|---|
 | 176 | +        public void noteAdd(Object origin)  | 
|---|
 | 177 | +        {  | 
|---|
 | 178 | +                setChangedSinceSave(true);  | 
|---|
 | 179 | +                updateActs();  | 
|---|
 | 180 | +                fireEvent(new TFEvent(TFEvent.Type.ACT_ADD, origin));  | 
|---|
 | 181 | +        }  | 
|---|
 | 182 | +  | 
|---|
 | 183 | +        public void setFilter(ActFilter filter, Object origin)  | 
|---|
 | 184 | +        {  | 
|---|
 | 185 | +                this.filter = filter;  | 
|---|
 | 186 | +                updateActs();  | 
|---|
 | 187 | +                fireEvent(new TFEvent(TFEvent.Type.FILTER_CHANGE, origin));  | 
|---|
 | 188 | +        }  | 
|---|
 | 189 | +  | 
|---|
 | 190 | +        private void updateActs()  | 
|---|
 | 191 | +        {  | 
|---|
 | 192 | +                acts = db.select(filter);  | 
|---|
 | 193 | +        }  | 
|---|
 | 194 | +  | 
|---|
 | 195 | +        public void setDB(ActDB db, String dbFile, boolean readOnly, Object origin)  | 
|---|
 | 196 | +        {  | 
|---|
 | 197 | +                this.db = db;  | 
|---|
 | 198 | +                this.dbFile = dbFile;  | 
|---|
 | 199 | +                this.readOnly = readOnly;  | 
|---|
 | 200 | +                setChangedSinceSave(false);  | 
|---|
 | 201 | +                this.filter = new ConstFilter(true);  | 
|---|
 | 202 | +                this.acts = db.all();  | 
|---|
 | 203 | +                initVisualEncodings();  | 
|---|
 | 204 | +                refreshColors();  | 
|---|
 | 205 | +                fireEvent(new TFEvent(TFEvent.Type.DATABASE_CHANGE, origin));  | 
|---|
 | 206 | +        }  | 
|---|
 | 207 | +  | 
|---|
 | 208 | +        private void initVisualEncodings()  | 
|---|
 | 209 | +        {  | 
|---|
 | 210 | +                guessField(VirtualField.LABEL, labelGuesses, String.class);  | 
|---|
 | 211 | +                guessField(VirtualField.START, startGuesses, RoughTime.class);  | 
|---|
 | 212 | +                viewInterval = null;  | 
|---|
 | 213 | +        }  | 
|---|
 | 214 | +  | 
|---|
 | 215 | +        public void refreshColors()  | 
|---|
 | 216 | +        {  | 
|---|
 | 217 | +                display.grayFilter = null;  | 
|---|
 | 218 | +                Field colorField = getColorField();  | 
|---|
 | 219 | +                if (colorField == null)  | 
|---|
 | 220 | +                {  | 
|---|
 | 221 | +                        return;  | 
|---|
 | 222 | +                }  | 
|---|
 | 223 | +                List<String> top25 = DBUtils.countValues(db, colorField).listTop(25);  | 
|---|
 | 224 | +                display.refreshColors(top25);  | 
|---|
 | 225 | +        }  | 
|---|
 | 226 | +  | 
|---|
 | 227 | +        private void guessField(String name, String[] guesses, Class type)  | 
|---|
 | 228 | +        {  | 
|---|
 | 229 | +                Field field = db.getField(name);  | 
|---|
 | 230 | +                if (field == null)  | 
|---|
 | 231 | +                {  | 
|---|
 | 232 | +                        for (int i = 0; i < guesses.length; i++)  | 
|---|
 | 233 | +                        {  | 
|---|
 | 234 | +                                Field f = db.getField(guesses[i]);  | 
|---|
 | 235 | +                                if (f != null && f.getType() == type)  | 
|---|
 | 236 | +                                {  | 
|---|
 | 237 | +                                        field = f;  | 
|---|
 | 238 | +                                        break;  | 
|---|
 | 239 | +                                }  | 
|---|
 | 240 | +                        }  | 
|---|
 | 241 | +                        if (field == null)  | 
|---|
 | 242 | +                        {  | 
|---|
 | 243 | +                                List<Field> f = db.getFields(type);  | 
|---|
 | 244 | +                                if (f.size() > 0)  | 
|---|
 | 245 | +                                {  | 
|---|
 | 246 | +                                        field = f.get(0);  | 
|---|
 | 247 | +                                }  | 
|---|
 | 248 | +                        }  | 
|---|
 | 249 | +                        if (field != null)  | 
|---|
 | 250 | +                        {  | 
|---|
 | 251 | +                                db.setAlias(field, name);  | 
|---|
 | 252 | +                        }  | 
|---|
 | 253 | +                }  | 
|---|
 | 254 | +        }  | 
|---|
 | 255 | +  | 
|---|
 | 256 | +        private void fireEvent(TFEvent e)  | 
|---|
 | 257 | +        {  | 
|---|
 | 258 | +                // clone list before going through it, because some events can cause  | 
|---|
 | 259 | +                // listeners to be added or removed.  | 
|---|
 | 260 | +  | 
|---|
 | 261 | +                for (TFListener t : (List<TFListener>) listeners.clone())  | 
|---|
 | 262 | +                {  | 
|---|
 | 263 | +                        if (t != e.origin)  | 
|---|
 | 264 | +                        {  | 
|---|
 | 265 | +                                t.note(e);  | 
|---|
 | 266 | +                        }  | 
|---|
 | 267 | +                }  | 
|---|
 | 268 | +        }  | 
|---|
 | 269 | +  | 
|---|
 | 270 | +        public void setFieldAlias(Field field, String alias, Object origin)  | 
|---|
 | 271 | +        {  | 
|---|
 | 272 | +                db.setAlias(field, alias);  | 
|---|
 | 273 | +                if (db.size() > 0 && field == getColorField())  | 
|---|
 | 274 | +                {  | 
|---|
 | 275 | +                        refreshColors();  | 
|---|
 | 276 | +                }  | 
|---|
 | 277 | +                fireEvent(new TFEvent(TFEvent.Type.VIEW_CHANGE, origin));  | 
|---|
 | 278 | +        }  | 
|---|
 | 279 | +  | 
|---|
 | 280 | +        public Field getColorField()  | 
|---|
 | 281 | +        {  | 
|---|
 | 282 | +                if (db == null)  | 
|---|
 | 283 | +                {  | 
|---|
 | 284 | +                        return null;  | 
|---|
 | 285 | +                }  | 
|---|
 | 286 | +                Field f = db.getField(VirtualField.COLOR);  | 
|---|
 | 287 | +                if (f != null)  | 
|---|
 | 288 | +                {  | 
|---|
 | 289 | +                        return f;  | 
|---|
 | 290 | +                }  | 
|---|
 | 291 | +                return db.getField(VirtualField.TRACK);  | 
|---|
 | 292 | +        }  | 
|---|
 | 293 | +  | 
|---|
 | 294 | +        public void setReadOnly(boolean readOnly)  | 
|---|
 | 295 | +        {  | 
|---|
 | 296 | +                this.readOnly = readOnly;  | 
|---|
 | 297 | +        }  | 
|---|
 | 298 | +  | 
|---|
 | 299 | +        public boolean isReadOnly()  | 
|---|
 | 300 | +        {  | 
|---|
 | 301 | +                return readOnly;  | 
|---|
 | 302 | +        }  | 
|---|
 | 303 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.model;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import java.util.*;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +public class VirtualField {  | 
|---|
 | 6 | +	public static final String LABEL="TIMEFLOW_LABEL";  | 
|---|
 | 7 | +	public static final String COLOR="TIMEFLOW_COLOR";  | 
|---|
 | 8 | +	public static final String SIZE="TIMEFLOW_SIZE";  | 
|---|
 | 9 | +	public static final String TRACK="TIMEFLOW_TRACK";  | 
|---|
 | 10 | +	public static final String START="TIMEFLOW_START";  | 
|---|
 | 11 | +	public static final String LATEST_START="TIMEFLOW_LATEST_START";  | 
|---|
 | 12 | +	public static final String END="TIMEFLOW_END";  | 
|---|
 | 13 | +	public static final String EARLIEST_END="TIMEFLOW_EARLIEST_END";  | 
|---|
 | 14 | +	  | 
|---|
 | 15 | +	private static HashMap<String, String> humanNames=new HashMap<String, String>();  | 
|---|
 | 16 | +	private static void tie(String a, String b) {humanNames.put(a,b);}  | 
|---|
 | 17 | +	  | 
|---|
 | 18 | +	static  | 
|---|
 | 19 | +	{  | 
|---|
 | 20 | +		tie(LABEL, "Label");  | 
|---|
 | 21 | +		tie(COLOR, "Color");  | 
|---|
 | 22 | +		tie(SIZE, "Size");  | 
|---|
 | 23 | +		tie(TRACK, "Track");  | 
|---|
 | 24 | +		tie(START, "Start");  | 
|---|
 | 25 | +		tie(LATEST_START, "Latest Start");  | 
|---|
 | 26 | +		tie(END, "End");  | 
|---|
 | 27 | +		tie(EARLIEST_END, "Earliest End");  | 
|---|
 | 28 | +	}  | 
|---|
 | 29 | +	  | 
|---|
 | 30 | +	public static String humanName(String s)  | 
|---|
 | 31 | +	{  | 
|---|
 | 32 | +		return humanNames.get(s);  | 
|---|
 | 33 | +	}  | 
|---|
 | 34 | +	  | 
|---|
 | 35 | +	public static Iterable<String> list()  | 
|---|
 | 36 | +	{  | 
|---|
 | 37 | +		return humanNames.keySet();  | 
|---|
 | 38 | +	}  | 
|---|
 | 39 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.util;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import java.util.*;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +public class Bag<T> implements Iterable<T> {  | 
|---|
 | 6 | +	HashMap<T, Count> table;  | 
|---|
 | 7 | +	int max;  | 
|---|
 | 8 | +	  | 
|---|
 | 9 | +	public Bag()  | 
|---|
 | 10 | +	{  | 
|---|
 | 11 | +		table=new HashMap<T, Count>();  | 
|---|
 | 12 | +	}  | 
|---|
 | 13 | +	  | 
|---|
 | 14 | +	public Bag(Iterable<T> i)  | 
|---|
 | 15 | +	{  | 
|---|
 | 16 | +		for (T x:i)  | 
|---|
 | 17 | +			add(x);  | 
|---|
 | 18 | +	}  | 
|---|
 | 19 | +	  | 
|---|
 | 20 | +	public Bag(T[] array)  | 
|---|
 | 21 | +	{  | 
|---|
 | 22 | +		for (int i=0; i<array.length; i++)  | 
|---|
 | 23 | +			add(array[i]);  | 
|---|
 | 24 | +	}  | 
|---|
 | 25 | +	  | 
|---|
 | 26 | +	public int getMax()  | 
|---|
 | 27 | +	{  | 
|---|
 | 28 | +		return max;  | 
|---|
 | 29 | +	}  | 
|---|
 | 30 | +	  | 
|---|
 | 31 | +	public List<T> listTop(int n)  | 
|---|
 | 32 | +	{  | 
|---|
 | 33 | +		int count=0;  | 
|---|
 | 34 | +		Iterator<T> i=list().iterator();  | 
|---|
 | 35 | +		List<T> top=new ArrayList<T>();  | 
|---|
 | 36 | +		while (count<n && i.hasNext())  | 
|---|
 | 37 | +		{  | 
|---|
 | 38 | +			top.add(i.next());  | 
|---|
 | 39 | +			count++;  | 
|---|
 | 40 | +		}  | 
|---|
 | 41 | +		return top;  | 
|---|
 | 42 | +	}  | 
|---|
 | 43 | +	  | 
|---|
 | 44 | +	public List<T> unordered()  | 
|---|
 | 45 | +	{  | 
|---|
 | 46 | +		List<T> result=new ArrayList<T>();  | 
|---|
 | 47 | +		result.addAll(table.keySet());  | 
|---|
 | 48 | +		return result;  | 
|---|
 | 49 | +	}  | 
|---|
 | 50 | +	  | 
|---|
 | 51 | +	public List<T> list()  | 
|---|
 | 52 | +	{  | 
|---|
 | 53 | +		List<T> result=new ArrayList<T>();  | 
|---|
 | 54 | +		result.addAll(table.keySet());  | 
|---|
 | 55 | +		  | 
|---|
 | 56 | +		Collections.sort(result, new Comparator<T>()  | 
|---|
 | 57 | +				{  | 
|---|
 | 58 | +					public int compare(T x, T y)  | 
|---|
 | 59 | +					{  | 
|---|
 | 60 | +						return num(y)-num(x);  | 
|---|
 | 61 | +					}  | 
|---|
 | 62 | +				});  | 
|---|
 | 63 | +		return result;  | 
|---|
 | 64 | +	}  | 
|---|
 | 65 | +	  | 
|---|
 | 66 | +	public int num(T x)  | 
|---|
 | 67 | +	{  | 
|---|
 | 68 | +		Count c=table.get(x);  | 
|---|
 | 69 | +		if (c!=null)  | 
|---|
 | 70 | +			return c.num;  | 
|---|
 | 71 | +		else  | 
|---|
 | 72 | +			return 0;  | 
|---|
 | 73 | +	}  | 
|---|
 | 74 | +	  | 
|---|
 | 75 | +	public int add(T x)  | 
|---|
 | 76 | +	{		  | 
|---|
 | 77 | +		Count c=table.get(x);  | 
|---|
 | 78 | +		int n=0;  | 
|---|
 | 79 | +		if (c!=null)  | 
|---|
 | 80 | +			n=++c.num;  | 
|---|
 | 81 | +		else  | 
|---|
 | 82 | +		{  | 
|---|
 | 83 | +			table.put(x, new Count(1));  | 
|---|
 | 84 | +			n=1;  | 
|---|
 | 85 | +		}  | 
|---|
 | 86 | +		max=Math.max(n,max);  | 
|---|
 | 87 | +		return n;  | 
|---|
 | 88 | +	}  | 
|---|
 | 89 | +	  | 
|---|
 | 90 | +	class Count  | 
|---|
 | 91 | +	{  | 
|---|
 | 92 | +		int num;  | 
|---|
 | 93 | +		public Count(int num)  | 
|---|
 | 94 | +		{  | 
|---|
 | 95 | +			this.num=num;  | 
|---|
 | 96 | +		}  | 
|---|
 | 97 | +	}  | 
|---|
 | 98 | +	  | 
|---|
 | 99 | +	public int size()  | 
|---|
 | 100 | +	{  | 
|---|
 | 101 | +		return table.size();  | 
|---|
 | 102 | +	}  | 
|---|
 | 103 | +	  | 
|---|
 | 104 | +	public int removeLessThan(int cut)  | 
|---|
 | 105 | +	{  | 
|---|
 | 106 | +		  | 
|---|
 | 107 | +		Set<T> small=new HashSet<T>();  | 
|---|
 | 108 | +		for (T x: table.keySet())  | 
|---|
 | 109 | +		{  | 
|---|
 | 110 | +			if (num(x)<cut)  | 
|---|
 | 111 | +				small.add(x);  | 
|---|
 | 112 | +		}  | 
|---|
 | 113 | +		for (T x:small)  | 
|---|
 | 114 | +			table.remove(x);  | 
|---|
 | 115 | +		return small.size();  | 
|---|
 | 116 | +	}  | 
|---|
 | 117 | +	  | 
|---|
 | 118 | +	public static void main(String[] args)  | 
|---|
 | 119 | +	{  | 
|---|
 | 120 | +		Bag<String> b=new Bag<String>();  | 
|---|
 | 121 | +		b.add("a");  | 
|---|
 | 122 | +		b.add("b");  | 
|---|
 | 123 | +		b.add("a");  | 
|---|
 | 124 | +		System.out.println(b.num("a"));  | 
|---|
 | 125 | +		System.out.println(b.num("b"));  | 
|---|
 | 126 | +		System.out.println(b.num("c"));  | 
|---|
 | 127 | +		List<String> s=b.list();  | 
|---|
 | 128 | +		for (int i=0; i<s.size(); i++)  | 
|---|
 | 129 | +			System.out.println(s.get(i)+": "+b.num(s.get(i)));  | 
|---|
 | 130 | +	}  | 
|---|
 | 131 | +  | 
|---|
 | 132 | +	@Override  | 
|---|
 | 133 | +	public Iterator<T> iterator() {  | 
|---|
 | 134 | +		return table.keySet().iterator();  | 
|---|
 | 135 | +	}  | 
|---|
 | 136 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.util;
  | 
|---|
 | 2 | +
  | 
|---|
 | 3 | +import java.awt.*;
  | 
|---|
 | 4 | +import java.awt.image.*;
  | 
|---|
 | 5 | +
  | 
|---|
 | 6 | +public class ColorUtils
  | 
|---|
 | 7 | +{
  | 
|---|
 | 8 | +	
  | 
|---|
 | 9 | +	public static Color alpha(Color c, int a)
  | 
|---|
 | 10 | +	{
  | 
|---|
 | 11 | +		return new Color(c.getRed(), c.getGreen(), c.getBlue(),a);
  | 
|---|
 | 12 | +	}
  | 
|---|
 | 13 | +	
  | 
|---|
 | 14 | +	public static Color interpolate(Color x, Color y, double u)
  | 
|---|
 | 15 | +	{
  | 
|---|
 | 16 | +		return new Color(interp(x.getRed(), y.getRed(), u), 
  | 
|---|
 | 17 | +				         interp(x.getGreen(), y.getGreen(), u),
  | 
|---|
 | 18 | +				         interp(x.getBlue(), y.getBlue(), u));
  | 
|---|
 | 19 | +	}
  | 
|---|
 | 20 | +	
  | 
|---|
 | 21 | +	private static int interp(int x, int y, double u)
  | 
|---|
 | 22 | +	{
  | 
|---|
 | 23 | +		return (int)(y*u+x*(1-u));
  | 
|---|
 | 24 | +	}
  | 
|---|
 | 25 | +	
  | 
|---|
 | 26 | +	public static float[] hsb(Color c)
  | 
|---|
 | 27 | +	{
  | 
|---|
 | 28 | +		return Color.RGBtoHSB(c.getRed(), c.getGreen(), c.getBlue(), new float[3]);
  | 
|---|
 | 29 | +	}
  | 
|---|
 | 30 | +	
  | 
|---|
 | 31 | +}
  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.util;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import java.util.*;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +public class DoubleBag<T> implements Iterable<T> {  | 
|---|
 | 6 | +	private HashMap<T, Count> table;  | 
|---|
 | 7 | +	private double max;  | 
|---|
 | 8 | +	  | 
|---|
 | 9 | +	public DoubleBag()  | 
|---|
 | 10 | +	{  | 
|---|
 | 11 | +		table=new HashMap<T, Count>();  | 
|---|
 | 12 | +	}  | 
|---|
 | 13 | +  | 
|---|
 | 14 | +	public double getMax()  | 
|---|
 | 15 | +	{  | 
|---|
 | 16 | +		return max;  | 
|---|
 | 17 | +	}  | 
|---|
 | 18 | +	  | 
|---|
 | 19 | +	public List<T> listTop(int n, boolean useSum)  | 
|---|
 | 20 | +	{  | 
|---|
 | 21 | +		int count=0;  | 
|---|
 | 22 | +		Iterator<T> i=list(useSum).iterator();  | 
|---|
 | 23 | +		List<T> top=new ArrayList<T>();  | 
|---|
 | 24 | +		while (count<n && i.hasNext())  | 
|---|
 | 25 | +		{  | 
|---|
 | 26 | +			top.add(i.next());  | 
|---|
 | 27 | +			count++;  | 
|---|
 | 28 | +		}  | 
|---|
 | 29 | +		return top;  | 
|---|
 | 30 | +	}  | 
|---|
 | 31 | +	  | 
|---|
 | 32 | +	public List<T> list(final boolean useSum)  | 
|---|
 | 33 | +	{  | 
|---|
 | 34 | +		List<T> result=new ArrayList<T>();  | 
|---|
 | 35 | +		result.addAll(table.keySet());  | 
|---|
 | 36 | +		  | 
|---|
 | 37 | +		Collections.sort(result, new Comparator<T>()  | 
|---|
 | 38 | +				{  | 
|---|
 | 39 | +					public int compare(T x, T y)  | 
|---|
 | 40 | +					{  | 
|---|
 | 41 | +						double d= useSum ? num(y)-num(x) : average(y)-average(x);  | 
|---|
 | 42 | +						return d>0 ? 1 : (d<0 ? -1 : 0);  | 
|---|
 | 43 | +					}  | 
|---|
 | 44 | +				});  | 
|---|
 | 45 | +		return result;  | 
|---|
 | 46 | +	}  | 
|---|
 | 47 | +	  | 
|---|
 | 48 | +	public double num(T x)  | 
|---|
 | 49 | +	{  | 
|---|
 | 50 | +		Count c=table.get(x);  | 
|---|
 | 51 | +		if (c!=null)  | 
|---|
 | 52 | +			return c.num;  | 
|---|
 | 53 | +		else  | 
|---|
 | 54 | +			return 0;  | 
|---|
 | 55 | +	}  | 
|---|
 | 56 | +	  | 
|---|
 | 57 | +	public double average(T x)  | 
|---|
 | 58 | +	{  | 
|---|
 | 59 | +		Count c=table.get(x);  | 
|---|
 | 60 | +		return c.num/c.vals;  | 
|---|
 | 61 | +	}  | 
|---|
 | 62 | +	  | 
|---|
 | 63 | +	public void add(T x, double z)  | 
|---|
 | 64 | +	{  | 
|---|
 | 65 | +		if (Double.isNaN(z))  | 
|---|
 | 66 | +			return;  | 
|---|
 | 67 | +		Count c=table.get(x);  | 
|---|
 | 68 | +		double sum=z;  | 
|---|
 | 69 | +		if (c!=null)  | 
|---|
 | 70 | +		{  | 
|---|
 | 71 | +			 c.add(z);  | 
|---|
 | 72 | +			 sum=c.num;  | 
|---|
 | 73 | +		}  | 
|---|
 | 74 | +		else  | 
|---|
 | 75 | +		{  | 
|---|
 | 76 | +			table.put(x, new Count(z));  | 
|---|
 | 77 | +		}  | 
|---|
 | 78 | +		max=Math.max(sum, max);  | 
|---|
 | 79 | +	}  | 
|---|
 | 80 | +	  | 
|---|
 | 81 | +	class Count  | 
|---|
 | 82 | +	{  | 
|---|
 | 83 | +		double num;  | 
|---|
 | 84 | +		int vals;  | 
|---|
 | 85 | +		  | 
|---|
 | 86 | +		public Count(double num)  | 
|---|
 | 87 | +		{  | 
|---|
 | 88 | +			this.num=num;  | 
|---|
 | 89 | +			vals=1;  | 
|---|
 | 90 | +		}  | 
|---|
 | 91 | +		  | 
|---|
 | 92 | +		public double add(double x)  | 
|---|
 | 93 | +		{  | 
|---|
 | 94 | +			vals++;  | 
|---|
 | 95 | +			return num+=x;  | 
|---|
 | 96 | +		}  | 
|---|
 | 97 | +	}  | 
|---|
 | 98 | +	  | 
|---|
 | 99 | +	public int size()  | 
|---|
 | 100 | +	{  | 
|---|
 | 101 | +		return table.size();  | 
|---|
 | 102 | +	}  | 
|---|
 | 103 | +	  | 
|---|
 | 104 | +	  | 
|---|
 | 105 | +	public List<T> unordered()  | 
|---|
 | 106 | +	{  | 
|---|
 | 107 | +		List<T> result=new ArrayList<T>();  | 
|---|
 | 108 | +		result.addAll(table.keySet());  | 
|---|
 | 109 | +		return result;  | 
|---|
 | 110 | +	}  | 
|---|
 | 111 | +  | 
|---|
 | 112 | +	  | 
|---|
 | 113 | +	public int removeLessThan(int cut)  | 
|---|
 | 114 | +	{  | 
|---|
 | 115 | +		  | 
|---|
 | 116 | +		Set<T> small=new HashSet<T>();  | 
|---|
 | 117 | +		for (T x: table.keySet())  | 
|---|
 | 118 | +		{  | 
|---|
 | 119 | +			if (num(x)<cut)  | 
|---|
 | 120 | +				small.add(x);  | 
|---|
 | 121 | +		}  | 
|---|
 | 122 | +		for (T x:small)  | 
|---|
 | 123 | +			table.remove(x);  | 
|---|
 | 124 | +		return small.size();  | 
|---|
 | 125 | +	}  | 
|---|
 | 126 | +	  | 
|---|
 | 127 | +	public static void main(String[] args)  | 
|---|
 | 128 | +	{  | 
|---|
 | 129 | +		DoubleBag<String> b=new DoubleBag<String>();  | 
|---|
 | 130 | +		b.add("a",1);  | 
|---|
 | 131 | +		b.add("b",2);  | 
|---|
 | 132 | +		b.add("a",3);  | 
|---|
 | 133 | +		System.out.println(b.num("a"));  | 
|---|
 | 134 | +		System.out.println(b.num("b"));  | 
|---|
 | 135 | +		System.out.println(b.num("c"));  | 
|---|
 | 136 | +		List<String> s=b.list(true);  | 
|---|
 | 137 | +		for (int i=0; i<s.size(); i++)  | 
|---|
 | 138 | +			System.out.println(s.get(i)+": "+b.num(s.get(i)));  | 
|---|
 | 139 | +	}  | 
|---|
 | 140 | +  | 
|---|
 | 141 | +	@Override  | 
|---|
 | 142 | +	public Iterator<T> iterator() {  | 
|---|
 | 143 | +		return table.keySet().iterator();  | 
|---|
 | 144 | +	}  | 
|---|
 | 145 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.util;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import java.io.*;  | 
|---|
 | 4 | +import java.util.*;  | 
|---|
 | 5 | +  | 
|---|
 | 6 | +public class IO {  | 
|---|
 | 7 | +	  | 
|---|
 | 8 | +	public static ArrayList<String> lines(String fileName) throws IOException  | 
|---|
 | 9 | +	{  | 
|---|
 | 10 | +		ArrayList<String> a=new ArrayList<String>();  | 
|---|
 | 11 | +		String line=null;  | 
|---|
 | 12 | +		FileReader fr=new FileReader(fileName);  | 
|---|
 | 13 | +		BufferedReader in=new BufferedReader(fr);  | 
|---|
 | 14 | +		while (null != (line=in.readLine()))  | 
|---|
 | 15 | +			a.add(line);  | 
|---|
 | 16 | +		in.close();  | 
|---|
 | 17 | +		fr.close();  | 
|---|
 | 18 | +		return a;  | 
|---|
 | 19 | +	}  | 
|---|
 | 20 | +	  | 
|---|
 | 21 | +	public static String[] lineArray(String fileName) throws IOException  | 
|---|
 | 22 | +	{  | 
|---|
 | 23 | +		ArrayList<String> a=lines(fileName);  | 
|---|
 | 24 | +		return (String[])a.toArray(new String[0]);  | 
|---|
 | 25 | +	}  | 
|---|
 | 26 | +	  | 
|---|
 | 27 | +	public static String read(File file) throws IOException  | 
|---|
 | 28 | +	{  | 
|---|
 | 29 | +		char[] buffer = new char[1024];  | 
|---|
 | 30 | +		int n = 0;  | 
|---|
 | 31 | +		StringBuilder builder = new StringBuilder();  | 
|---|
 | 32 | +		FileReader reader = new FileReader(file);  | 
|---|
 | 33 | +		BufferedReader b = new BufferedReader(reader);  | 
|---|
 | 34 | +		while ((n = b.read(buffer, 0, buffer.length)) != -1)   | 
|---|
 | 35 | +			builder.append(buffer, 0, n);  | 
|---|
 | 36 | +		b.close();  | 
|---|
 | 37 | +		reader.close();  | 
|---|
 | 38 | +		return builder.toString();  | 
|---|
 | 39 | +	}  | 
|---|
 | 40 | +	  | 
|---|
 | 41 | +	public static String read(String fileName) throws IOException  | 
|---|
 | 42 | +	{  | 
|---|
 | 43 | +		char[] buffer = new char[1024];  | 
|---|
 | 44 | +		int n = 0;  | 
|---|
 | 45 | +		StringBuilder builder = new StringBuilder();  | 
|---|
 | 46 | +		FileReader reader = new FileReader(fileName);  | 
|---|
 | 47 | +		BufferedReader b = new BufferedReader(reader);  | 
|---|
 | 48 | +		while ((n = b.read(buffer, 0, buffer.length)) != -1)   | 
|---|
 | 49 | +			builder.append(buffer, 0, n);  | 
|---|
 | 50 | +		b.close();  | 
|---|
 | 51 | +		reader.close();  | 
|---|
 | 52 | +		return builder.toString();  | 
|---|
 | 53 | +	}  | 
|---|
 | 54 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.util;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import java.awt.*;  | 
|---|
 | 4 | +import javax.swing.*;  | 
|---|
 | 5 | +  | 
|---|
 | 6 | +public class Pad extends JPanel {  | 
|---|
 | 7 | +	Dimension pref;  | 
|---|
 | 8 | +	  | 
|---|
 | 9 | +	public Pad(int w, int h)  | 
|---|
 | 10 | +	{  | 
|---|
 | 11 | +		pref=new Dimension(w,h);  | 
|---|
 | 12 | +		setBackground(Color.white);  | 
|---|
 | 13 | +	}  | 
|---|
 | 14 | +	  | 
|---|
 | 15 | +	public Dimension getPreferredSize()  | 
|---|
 | 16 | +	{  | 
|---|
 | 17 | +		return pref;  | 
|---|
 | 18 | +	}  | 
|---|
 | 19 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.util;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import java.util.*;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +public class TimeIt {  | 
|---|
 | 6 | +	public static long last;  | 
|---|
 | 7 | +	static HashMap<Object, Long> marks=new HashMap<Object, Long>();  | 
|---|
 | 8 | +	  | 
|---|
 | 9 | +	public static void mark()  | 
|---|
 | 10 | +	{  | 
|---|
 | 11 | +		last=System.currentTimeMillis();  | 
|---|
 | 12 | +	}  | 
|---|
 | 13 | +	  | 
|---|
 | 14 | +	public static void sinceLast()  | 
|---|
 | 15 | +	{  | 
|---|
 | 16 | +		long now=System.currentTimeMillis();  | 
|---|
 | 17 | +		System.out.println("TimeIt: "+(now-last));  | 
|---|
 | 18 | +		last=now;  | 
|---|
 | 19 | +	}  | 
|---|
 | 20 | +	  | 
|---|
 | 21 | +	public static void since(Object o)  | 
|---|
 | 22 | +	{  | 
|---|
 | 23 | +		long now=System.currentTimeMillis();  | 
|---|
 | 24 | +		System.out.println("TimeIt: "+o+": "+(now-last));  | 
|---|
 | 25 | +		last=now;  | 
|---|
 | 26 | +	}  | 
|---|
 | 27 | +	  | 
|---|
 | 28 | +	public static void mark(Object o)  | 
|---|
 | 29 | +	{  | 
|---|
 | 30 | +		long now=System.currentTimeMillis();  | 
|---|
 | 31 | +		marks.put(o, System.currentTimeMillis());  | 
|---|
 | 32 | +		last=now;  | 
|---|
 | 33 | +	}  | 
|---|
 | 34 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.views;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import javax.swing.*;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +import timeflow.data.db.ActDB;  | 
|---|
 | 6 | +import timeflow.model.*;  | 
|---|
 | 7 | +  | 
|---|
 | 8 | +import java.awt.*;  | 
|---|
 | 9 | +  | 
|---|
 | 10 | +// superclass of all timeline views  | 
|---|
 | 11 | +public abstract class AbstractView extends ModelPanel  | 
|---|
 | 12 | +{  | 
|---|
 | 13 | +  | 
|---|
 | 14 | +        protected boolean ignoreEventsWhenInvisible = true;  | 
|---|
 | 15 | +        JPanel panel;  | 
|---|
 | 16 | +        ActDB lastDrawn, lastNotified;  | 
|---|
 | 17 | +  | 
|---|
 | 18 | +        public AbstractView(TFModel model)  | 
|---|
 | 19 | +        {  | 
|---|
 | 20 | +                super(model);  | 
|---|
 | 21 | +        }  | 
|---|
 | 22 | +  | 
|---|
 | 23 | +        public void paintComponent(Graphics g)  | 
|---|
 | 24 | +        {  | 
|---|
 | 25 | +                g.drawString(getName(), 10, 30);  | 
|---|
 | 26 | +        }  | 
|---|
 | 27 | +  | 
|---|
 | 28 | +        public final JComponent getControls()  | 
|---|
 | 29 | +        {  | 
|---|
 | 30 | +                if (panel != null)  | 
|---|
 | 31 | +                {  | 
|---|
 | 32 | +                        return panel;  | 
|---|
 | 33 | +                }  | 
|---|
 | 34 | +  | 
|---|
 | 35 | +                panel = new JPanel();  | 
|---|
 | 36 | +                panel.setLayout(new BorderLayout());  | 
|---|
 | 37 | +                panel.add(_getControls(), BorderLayout.CENTER);  | 
|---|
 | 38 | +                JLabel controlLabel = new JLabel(" " + getName() + " Controls")  | 
|---|
 | 39 | +                {  | 
|---|
 | 40 | +  | 
|---|
 | 41 | +                        public Dimension getPreferredSize()  | 
|---|
 | 42 | +                        {  | 
|---|
 | 43 | +                                return new Dimension(30, 30);  | 
|---|
 | 44 | +                        }  | 
|---|
 | 45 | +                };  | 
|---|
 | 46 | +                controlLabel.setBackground(Color.lightGray);  | 
|---|
 | 47 | +                controlLabel.setForeground(Color.darkGray);  | 
|---|
 | 48 | +                panel.add(controlLabel, BorderLayout.NORTH);  | 
|---|
 | 49 | +  | 
|---|
 | 50 | +                return panel;  | 
|---|
 | 51 | +        }  | 
|---|
 | 52 | +  | 
|---|
 | 53 | +        protected JComponent _getControls()  | 
|---|
 | 54 | +        {  | 
|---|
 | 55 | +                return new JLabel("local: " + getName());  | 
|---|
 | 56 | +        }  | 
|---|
 | 57 | +  | 
|---|
 | 58 | +        public abstract String getName();  | 
|---|
 | 59 | +  | 
|---|
 | 60 | +        protected abstract void _note(TFEvent e);  | 
|---|
 | 61 | +  | 
|---|
 | 62 | +        protected abstract void onscreen(boolean majorChangeHappened);  | 
|---|
 | 63 | +  | 
|---|
 | 64 | +        @Override  | 
|---|
 | 65 | +        public final void note(TFEvent e)  | 
|---|
 | 66 | +        {  | 
|---|
 | 67 | +                lastNotified = getModel().getDB();  | 
|---|
 | 68 | +                if (isVisible() || !ignoreEventsWhenInvisible)  | 
|---|
 | 69 | +                {  | 
|---|
 | 70 | +                        _note(e);  | 
|---|
 | 71 | +                        lastDrawn = lastNotified;  | 
|---|
 | 72 | +                }  | 
|---|
 | 73 | +        }  | 
|---|
 | 74 | +  | 
|---|
 | 75 | +        @Override  | 
|---|
 | 76 | +        public void setVisible(boolean visible)  | 
|---|
 | 77 | +        {  | 
|---|
 | 78 | +                super.setVisible(visible);  | 
|---|
 | 79 | +                if (visible && getModel().getDB() != null)  | 
|---|
 | 80 | +                {  | 
|---|
 | 81 | +                        onscreen(lastDrawn != lastNotified);  | 
|---|
 | 82 | +                        lastDrawn = lastNotified;  | 
|---|
 | 83 | +                }  | 
|---|
 | 84 | +        }  | 
|---|
 | 85 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.views;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.data.db.*;  | 
|---|
 | 4 | +import timeflow.data.time.*;  | 
|---|
 | 5 | +import timeflow.model.*;  | 
|---|
 | 6 | +import timeflow.vis.*;  | 
|---|
 | 7 | +import timeflow.app.ui.*;  | 
|---|
 | 8 | +  | 
|---|
 | 9 | +import java.awt.*;  | 
|---|
 | 10 | +import java.awt.event.*;  | 
|---|
 | 11 | +import java.awt.image.BufferedImage;  | 
|---|
 | 12 | +import java.net.URL;  | 
|---|
 | 13 | +import java.util.*;  | 
|---|
 | 14 | +  | 
|---|
 | 15 | +import javax.swing.*;  | 
|---|
 | 16 | +  | 
|---|
 | 17 | +public abstract class AbstractVisualizationView extends JPanel  | 
|---|
 | 18 | +{  | 
|---|
 | 19 | +  | 
|---|
 | 20 | +        Image buffer;  | 
|---|
 | 21 | +        Graphics2D graphics;  | 
|---|
 | 22 | +        Point mouse = new Point(-10000, 0), firstMouse = new Point();  | 
|---|
 | 23 | +        boolean mouseIsDown;  | 
|---|
 | 24 | +        ArrayList<Mouseover> objectLocations = new ArrayList<Mouseover>();  | 
|---|
 | 25 | +        TFModel model;  | 
|---|
 | 26 | +        Act selectedAct;  | 
|---|
 | 27 | +        RoughTime selectedTime;  | 
|---|
 | 28 | +        Set<JMenuItem> urlItems = new HashSet<JMenuItem>();  | 
|---|
 | 29 | +  | 
|---|
 | 30 | +        public AbstractVisualizationView(TFModel model)  | 
|---|
 | 31 | +        {  | 
|---|
 | 32 | +                this.model = model;  | 
|---|
 | 33 | +  | 
|---|
 | 34 | +                // deal with mouseovers.  | 
|---|
 | 35 | +                addMouseMotionListener(new MouseMotionListener()  | 
|---|
 | 36 | +                {  | 
|---|
 | 37 | +  | 
|---|
 | 38 | +                        @Override  | 
|---|
 | 39 | +                        public void mouseDragged(MouseEvent e)  | 
|---|
 | 40 | +                        {  | 
|---|
 | 41 | +                                mouse.setLocation(e.getX(), e.getY());  | 
|---|
 | 42 | +                                repaint();  | 
|---|
 | 43 | +                        }  | 
|---|
 | 44 | +  | 
|---|
 | 45 | +                        @Override  | 
|---|
 | 46 | +                        public void mouseMoved(MouseEvent e)  | 
|---|
 | 47 | +                        {  | 
|---|
 | 48 | +                                mouse.setLocation(e.getX(), e.getY());  | 
|---|
 | 49 | +                                repaint();  | 
|---|
 | 50 | +                        }  | 
|---|
 | 51 | +                });  | 
|---|
 | 52 | +  | 
|---|
 | 53 | +  | 
|---|
 | 54 | +                final JPopupMenu popup = new JPopupMenu();  | 
|---|
 | 55 | +                final JMenuItem edit = new JMenuItem("Edit");  | 
|---|
 | 56 | +                edit.addActionListener(new ActionListener()  | 
|---|
 | 57 | +                {  | 
|---|
 | 58 | +  | 
|---|
 | 59 | +                        @Override  | 
|---|
 | 60 | +                        public void actionPerformed(ActionEvent e)  | 
|---|
 | 61 | +                        {  | 
|---|
 | 62 | +                                EditRecordPanel.edit(getModel(), selectedAct);  | 
|---|
 | 63 | +                        }  | 
|---|
 | 64 | +                });  | 
|---|
 | 65 | +                popup.add(edit);  | 
|---|
 | 66 | +  | 
|---|
 | 67 | +                final JMenuItem delete = new JMenuItem("Delete");  | 
|---|
 | 68 | +                popup.add(delete);  | 
|---|
 | 69 | +                delete.addActionListener(new ActionListener()  | 
|---|
 | 70 | +                {  | 
|---|
 | 71 | +  | 
|---|
 | 72 | +                        @Override  | 
|---|
 | 73 | +                        public void actionPerformed(ActionEvent e)  | 
|---|
 | 74 | +                        {  | 
|---|
 | 75 | +                                getModel().getDB().delete(selectedAct);  | 
|---|
 | 76 | +                                getModel().noteDelete(this);  | 
|---|
 | 77 | +                        }  | 
|---|
 | 78 | +                });  | 
|---|
 | 79 | +  | 
|---|
 | 80 | +                final JMenuItem add = new JMenuItem("New...");  | 
|---|
 | 81 | +                popup.add(add);  | 
|---|
 | 82 | +                add.addActionListener(new ActionListener()  | 
|---|
 | 83 | +                {  | 
|---|
 | 84 | +  | 
|---|
 | 85 | +                        @Override  | 
|---|
 | 86 | +                        public void actionPerformed(ActionEvent e)  | 
|---|
 | 87 | +                        {  | 
|---|
 | 88 | +                                EditRecordPanel.add(getModel(), selectedTime);  | 
|---|
 | 89 | +                        }  | 
|---|
 | 90 | +                });  | 
|---|
 | 91 | +  | 
|---|
 | 92 | +                // deal with right-click.  | 
|---|
 | 93 | +                addMouseListener(new MouseAdapter()  | 
|---|
 | 94 | +                {  | 
|---|
 | 95 | +  | 
|---|
 | 96 | +                        public void mousePressed(MouseEvent e)  | 
|---|
 | 97 | +                        {  | 
|---|
 | 98 | +                                pop(e);  | 
|---|
 | 99 | +                        }  | 
|---|
 | 100 | +  | 
|---|
 | 101 | +                        public void mouseReleased(MouseEvent e)  | 
|---|
 | 102 | +                        {  | 
|---|
 | 103 | +                                pop(e);  | 
|---|
 | 104 | +                        }  | 
|---|
 | 105 | +  | 
|---|
 | 106 | +                        private void pop(MouseEvent e)  | 
|---|
 | 107 | +                        {  | 
|---|
 | 108 | +                                if (e.isPopupTrigger())  | 
|---|
 | 109 | +                                {  | 
|---|
 | 110 | +                                        Point p = new Point(e.getX(), e.getY());  | 
|---|
 | 111 | +                                        Mouseover o = find(p);  | 
|---|
 | 112 | +                                        boolean onAct = o != null && o.thing instanceof VisualAct;  | 
|---|
 | 113 | +                                        if (onAct)  | 
|---|
 | 114 | +                                        {  | 
|---|
 | 115 | +                                                VisualAct v = (VisualAct) o.thing;  | 
|---|
 | 116 | +                                                selectedAct = v.getAct();  | 
|---|
 | 117 | +                                                String name = " '" + v.getLabel() + "'";  | 
|---|
 | 118 | +                                                edit.setText("Edit" + name + "...");  | 
|---|
 | 119 | +                                                delete.setText("Delete" + name);  | 
|---|
 | 120 | +                                                edit.setEnabled(true);  | 
|---|
 | 121 | +                                                delete.setEnabled(true);  | 
|---|
 | 122 | +                                        } else  | 
|---|
 | 123 | +                                        {  | 
|---|
 | 124 | +                                                edit.setEnabled(false);  | 
|---|
 | 125 | +                                                edit.setText("Edit Event");  | 
|---|
 | 126 | +                                                delete.setEnabled(false);  | 
|---|
 | 127 | +                                                delete.setText("Delete Event");  | 
|---|
 | 128 | +                                        }  | 
|---|
 | 129 | +                                        selectedTime = getTime(p);  | 
|---|
 | 130 | +                                        if (selectedTime != null || onAct)  | 
|---|
 | 131 | +                                        {  | 
|---|
 | 132 | +                                                add.setEnabled(selectedTime != null);  | 
|---|
 | 133 | +                                                add.setText(selectedTime == null ? "Add" : "Add Event At " + selectedTime.format() + "...");  | 
|---|
 | 134 | +  | 
|---|
 | 135 | +                                                java.util.List<Field> urlFields = getModel().getDB().getFields(URL.class);  | 
|---|
 | 136 | +                                                if (urlFields.size() > 0)  | 
|---|
 | 137 | +                                                {  | 
|---|
 | 138 | +                                                        // remove any old items.  | 
|---|
 | 139 | +                                                        for (JMenuItem m : urlItems)  | 
|---|
 | 140 | +                                                        {  | 
|---|
 | 141 | +                                                                popup.remove(m);  | 
|---|
 | 142 | +                                                        }  | 
|---|
 | 143 | +                                                        urlItems.clear();  | 
|---|
 | 144 | +  | 
|---|
 | 145 | +                                                        if (onAct)  | 
|---|
 | 146 | +                                                        {  | 
|---|
 | 147 | +                                                                Act a = ((VisualAct) o.thing).getAct();  | 
|---|
 | 148 | +                                                                for (Field f : urlFields)  | 
|---|
 | 149 | +                                                                {  | 
|---|
 | 150 | +                                                                        final URL url = a.getURL(f);  | 
|---|
 | 151 | +                                                                        JMenuItem go = new JMenuItem("Go to " + url);  | 
|---|
 | 152 | +                                                                        go.addActionListener(new ActionListener()  | 
|---|
 | 153 | +                                                                        {  | 
|---|
 | 154 | +  | 
|---|
 | 155 | +                                                                                @Override  | 
|---|
 | 156 | +                                                                                public void actionPerformed(ActionEvent e)  | 
|---|
 | 157 | +                                                                                {  | 
|---|
 | 158 | +                                                                                        Display.launchBrowser(url.toString());  | 
|---|
 | 159 | +                                                                                }  | 
|---|
 | 160 | +                                                                        });  | 
|---|
 | 161 | +                                                                        popup.add(go);  | 
|---|
 | 162 | +                                                                        urlItems.add(go);  | 
|---|
 | 163 | +                                                                }  | 
|---|
 | 164 | +                                                        }  | 
|---|
 | 165 | +                                                }  | 
|---|
 | 166 | +  | 
|---|
 | 167 | +                                                popup.show(e.getComponent(), p.x, p.y);  | 
|---|
 | 168 | +                                        }  | 
|---|
 | 169 | +                                }  | 
|---|
 | 170 | +                        }  | 
|---|
 | 171 | +                });  | 
|---|
 | 172 | +        }  | 
|---|
 | 173 | +  | 
|---|
 | 174 | +        public RoughTime getTime(Point p)  | 
|---|
 | 175 | +        {  | 
|---|
 | 176 | +                return null;  | 
|---|
 | 177 | +        }  | 
|---|
 | 178 | +  | 
|---|
 | 179 | +        public TFModel getModel()  | 
|---|
 | 180 | +        {  | 
|---|
 | 181 | +                return model;  | 
|---|
 | 182 | +        }  | 
|---|
 | 183 | +  | 
|---|
 | 184 | +        @Override  | 
|---|
 | 185 | +        public void setBounds(int x, int y, int w, int h)  | 
|---|
 | 186 | +        {  | 
|---|
 | 187 | +                super.setBounds(x, y, w, h);  | 
|---|
 | 188 | +                if (w > 0 && h > 0)  | 
|---|
 | 189 | +                {  | 
|---|
 | 190 | +                        if (graphics != null)  | 
|---|
 | 191 | +                        {  | 
|---|
 | 192 | +                                graphics.dispose();  | 
|---|
 | 193 | +                        }  | 
|---|
 | 194 | +                        buffer = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);  | 
|---|
 | 195 | +                        graphics = (Graphics2D) buffer.getGraphics();  | 
|---|
 | 196 | +                        drawVisualization();  | 
|---|
 | 197 | +                        repaint();  | 
|---|
 | 198 | +                }  | 
|---|
 | 199 | +  | 
|---|
 | 200 | +        }  | 
|---|
 | 201 | +  | 
|---|
 | 202 | +        void drawVisualization()  | 
|---|
 | 203 | +        {  | 
|---|
 | 204 | +                drawVisualization(graphics);  | 
|---|
 | 205 | +        }  | 
|---|
 | 206 | +  | 
|---|
 | 207 | +        protected abstract void drawVisualization(Graphics2D g);  | 
|---|
 | 208 | +  | 
|---|
 | 209 | +        protected boolean paintOnTop(Graphics2D g, int w, int h)  | 
|---|
 | 210 | +        {  | 
|---|
 | 211 | +                return false;  | 
|---|
 | 212 | +        }  | 
|---|
 | 213 | +  | 
|---|
 | 214 | +        protected Mouseover find(Point p)  | 
|---|
 | 215 | +        {  | 
|---|
 | 216 | +                for (Mouseover o : objectLocations)  | 
|---|
 | 217 | +                {  | 
|---|
 | 218 | +                        if (o.contains(mouse))  | 
|---|
 | 219 | +                        {  | 
|---|
 | 220 | +                                return o;  | 
|---|
 | 221 | +                        }  | 
|---|
 | 222 | +                }  | 
|---|
 | 223 | +                return null;  | 
|---|
 | 224 | +        }  | 
|---|
 | 225 | +  | 
|---|
 | 226 | +        public final void paintComponent(Graphics g1)  | 
|---|
 | 227 | +        {  | 
|---|
 | 228 | +                Graphics2D g = (Graphics2D) g1;  | 
|---|
 | 229 | +                g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);  | 
|---|
 | 230 | +                g.drawImage(buffer, 0, 0, null);  | 
|---|
 | 231 | +                  | 
|---|
 | 232 | +                int w = getSize().width, h = getSize().height;  | 
|---|
 | 233 | +                if (paintOnTop(g, w, h))  | 
|---|
 | 234 | +                {  | 
|---|
 | 235 | +                        return;  | 
|---|
 | 236 | +                }  | 
|---|
 | 237 | +                  | 
|---|
 | 238 | +                Mouseover highlight = find(mouse);  | 
|---|
 | 239 | +                if (highlight != null)  | 
|---|
 | 240 | +                {  | 
|---|
 | 241 | +                        highlight.draw(g, w, h, getModel().getDisplay());  | 
|---|
 | 242 | +                }  | 
|---|
 | 243 | +        }  | 
|---|
 | 244 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.views;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.model.*;  | 
|---|
 | 4 | +import timeflow.views.ListView.LinkIt;  | 
|---|
 | 5 | +import timeflow.data.db.*;  | 
|---|
 | 6 | +import timeflow.data.time.*;  | 
|---|
 | 7 | +  | 
|---|
 | 8 | +import javax.swing.*;  | 
|---|
 | 9 | +  | 
|---|
 | 10 | +import timeflow.util.*;  | 
|---|
 | 11 | +  | 
|---|
 | 12 | +import java.awt.*;  | 
|---|
 | 13 | +import java.awt.event.*;  | 
|---|
 | 14 | +import java.util.*;  | 
|---|
 | 15 | +  | 
|---|
 | 16 | +public class BarGraphView extends AbstractView  | 
|---|
 | 17 | +{  | 
|---|
 | 18 | +  | 
|---|
 | 19 | +        BarGraph graph = new BarGraph();  | 
|---|
 | 20 | +        JPanel controls;  | 
|---|
 | 21 | +        ArrayList<BarData> bars;  | 
|---|
 | 22 | +  | 
|---|
 | 23 | +        enum Aggregate  | 
|---|
 | 24 | +        {  | 
|---|
 | 25 | +  | 
|---|
 | 26 | +                TOTAL, AVERAGE, COUNT  | 
|---|
 | 27 | +        };  | 
|---|
 | 28 | +        Aggregate agg;  | 
|---|
 | 29 | +        JComboBox splitFieldChoice, numFieldChoice;  | 
|---|
 | 30 | +  | 
|---|
 | 31 | +        public BarGraphView(TFModel model)  | 
|---|
 | 32 | +        {  | 
|---|
 | 33 | +                super(model);  | 
|---|
 | 34 | +  | 
|---|
 | 35 | +                setLayout(new BorderLayout());  | 
|---|
 | 36 | +                controls = new JPanel();  | 
|---|
 | 37 | +                add(controls, BorderLayout.NORTH);  | 
|---|
 | 38 | +                controls.setLayout(null);  | 
|---|
 | 39 | +                controls.setBackground(Color.white);  | 
|---|
 | 40 | +  | 
|---|
 | 41 | +                JScrollPane scrollPane = new JScrollPane(graph);  | 
|---|
 | 42 | +                add(scrollPane, BorderLayout.CENTER);  | 
|---|
 | 43 | +  | 
|---|
 | 44 | +                makeTop();  | 
|---|
 | 45 | +        }  | 
|---|
 | 46 | +  | 
|---|
 | 47 | +        protected JComponent _getControls()  | 
|---|
 | 48 | +        {  | 
|---|
 | 49 | +                return controls;  | 
|---|
 | 50 | +        }  | 
|---|
 | 51 | +  | 
|---|
 | 52 | +        void makeTop()  | 
|---|
 | 53 | +        {  | 
|---|
 | 54 | +                int x = 10, y = 10;  | 
|---|
 | 55 | +                int ch = 25, pad = 5, cw = 160;  | 
|---|
 | 56 | +  | 
|---|
 | 57 | +                controls.removeAll();  | 
|---|
 | 58 | +                TFModel model = getModel();  | 
|---|
 | 59 | +                if (model.getDB() == null || model.getDB().size() == 0)  | 
|---|
 | 60 | +                {  | 
|---|
 | 61 | +                        JLabel empty = new JLabel("Empty database");  | 
|---|
 | 62 | +                        controls.add(empty);  | 
|---|
 | 63 | +                        empty.setBounds(x, y, cw, ch);  | 
|---|
 | 64 | +                        return;  | 
|---|
 | 65 | +                }  | 
|---|
 | 66 | +  | 
|---|
 | 67 | +                JLabel top = new JLabel("For each value of");  | 
|---|
 | 68 | +                controls.add(top);  | 
|---|
 | 69 | +                top.setBounds(x, y, cw, ch);  | 
|---|
 | 70 | +                y += ch + pad;  | 
|---|
 | 71 | +  | 
|---|
 | 72 | +                splitFieldChoice = new JComboBox();  | 
|---|
 | 73 | +                String splitSelection = null;  | 
|---|
 | 74 | +                for (Field f : DBUtils.categoryFields(model.getDB()))  | 
|---|
 | 75 | +                {  | 
|---|
 | 76 | +                        splitFieldChoice.addItem(f.getName());  | 
|---|
 | 77 | +                        if (f == graph.splitField)  | 
|---|
 | 78 | +                        {  | 
|---|
 | 79 | +                                splitSelection = f.getName();  | 
|---|
 | 80 | +                        }  | 
|---|
 | 81 | +                }  | 
|---|
 | 82 | +                controls.add(splitFieldChoice);  | 
|---|
 | 83 | +                splitFieldChoice.setBounds(x, y, cw, ch);  | 
|---|
 | 84 | +                y += ch + 3 * pad;  | 
|---|
 | 85 | +  | 
|---|
 | 86 | +                if (splitSelection != null)  | 
|---|
 | 87 | +                {  | 
|---|
 | 88 | +                        splitFieldChoice.setSelectedItem(splitSelection);  | 
|---|
 | 89 | +                } else if (getModel().getColorField() != null)  | 
|---|
 | 90 | +                {  | 
|---|
 | 91 | +                        splitFieldChoice.setSelectedItem(getModel().getColorField().getName());  | 
|---|
 | 92 | +                }  | 
|---|
 | 93 | +                splitFieldChoice.addActionListener(new ActionListener()  | 
|---|
 | 94 | +                {  | 
|---|
 | 95 | +  | 
|---|
 | 96 | +                        @Override  | 
|---|
 | 97 | +                        public void actionPerformed(ActionEvent e)  | 
|---|
 | 98 | +                        {  | 
|---|
 | 99 | +                                graph.redo();  | 
|---|
 | 100 | +                        }  | 
|---|
 | 101 | +                });  | 
|---|
 | 102 | +  | 
|---|
 | 103 | +                JLabel showLabel = new JLabel("show");  | 
|---|
 | 104 | +                controls.add(showLabel);  | 
|---|
 | 105 | +                showLabel.setBounds(x, y, cw, ch);  | 
|---|
 | 106 | +                y += ch + pad;  | 
|---|
 | 107 | +  | 
|---|
 | 108 | +                numFieldChoice = new JComboBox();  | 
|---|
 | 109 | +                numFieldChoice.addItem("Number of events");  | 
|---|
 | 110 | +                final ArrayList<Field> valueFields = new ArrayList<Field>();  | 
|---|
 | 111 | +                for (Field f : model.getDB().getFields(Double.class))  | 
|---|
 | 112 | +                {  | 
|---|
 | 113 | +                        numFieldChoice.addItem("Total: " + f.getName());  | 
|---|
 | 114 | +                        numFieldChoice.addItem("Average: " + f.getName());  | 
|---|
 | 115 | +                        valueFields.add(f);  | 
|---|
 | 116 | +                }  | 
|---|
 | 117 | +                controls.add(numFieldChoice);  | 
|---|
 | 118 | +                numFieldChoice.setBounds(x, y, cw, ch);  | 
|---|
 | 119 | +  | 
|---|
 | 120 | +                boolean chosen = false;  | 
|---|
 | 121 | +                for (int i = 0; i < numFieldChoice.getItemCount(); i++)  | 
|---|
 | 122 | +                {  | 
|---|
 | 123 | +                        if (numFieldChoice.getItemAt(i).equals(graph.lastValueMenuChoice))  | 
|---|
 | 124 | +                        {  | 
|---|
 | 125 | +                                numFieldChoice.setSelectedIndex(i);  | 
|---|
 | 126 | +                                chosen = true;  | 
|---|
 | 127 | +                        }  | 
|---|
 | 128 | +                }  | 
|---|
 | 129 | +                if (!chosen)  | 
|---|
 | 130 | +                {  | 
|---|
 | 131 | +                        Field size = getModel().getDB().getField(VirtualField.SIZE);  | 
|---|
 | 132 | +                        if (size != null)  | 
|---|
 | 133 | +                        {  | 
|---|
 | 134 | +                                numFieldChoice.setSelectedItem("Total: " + size.getName());  | 
|---|
 | 135 | +                        }  | 
|---|
 | 136 | +                }  | 
|---|
 | 137 | +                numFieldChoice.addActionListener(new ActionListener()  | 
|---|
 | 138 | +                {  | 
|---|
 | 139 | +  | 
|---|
 | 140 | +                        @Override  | 
|---|
 | 141 | +                        public void actionPerformed(ActionEvent e)  | 
|---|
 | 142 | +                        {  | 
|---|
 | 143 | +  | 
|---|
 | 144 | +                                graph.redo();  | 
|---|
 | 145 | +                        }  | 
|---|
 | 146 | +                });  | 
|---|
 | 147 | +                revalidate();  | 
|---|
 | 148 | +                repaint();  | 
|---|
 | 149 | +        }  | 
|---|
 | 150 | +  | 
|---|
 | 151 | +        void reset()  | 
|---|
 | 152 | +        {  | 
|---|
 | 153 | +                makeTop();  | 
|---|
 | 154 | +                graph.redo();  | 
|---|
 | 155 | +                revalidate();  | 
|---|
 | 156 | +                repaint();  | 
|---|
 | 157 | +        }  | 
|---|
 | 158 | +  | 
|---|
 | 159 | +        @Override  | 
|---|
 | 160 | +        protected void _note(TFEvent e)  | 
|---|
 | 161 | +        {  | 
|---|
 | 162 | +                if (e.affectsSchema())  | 
|---|
 | 163 | +                {  | 
|---|
 | 164 | +                        reset();  | 
|---|
 | 165 | +                } else  | 
|---|
 | 166 | +                {  | 
|---|
 | 167 | +                        graph.redo();  | 
|---|
 | 168 | +                }  | 
|---|
 | 169 | +                repaint();  | 
|---|
 | 170 | +        }  | 
|---|
 | 171 | +  | 
|---|
 | 172 | +        @Override  | 
|---|
 | 173 | +        public String getName()  | 
|---|
 | 174 | +        {  | 
|---|
 | 175 | +                return "Bar Graph";  | 
|---|
 | 176 | +        }  | 
|---|
 | 177 | +  | 
|---|
 | 178 | +        @Override  | 
|---|
 | 179 | +        protected void onscreen(boolean majorChange)  | 
|---|
 | 180 | +        {  | 
|---|
 | 181 | +                reset();  | 
|---|
 | 182 | +        }  | 
|---|
 | 183 | +  | 
|---|
 | 184 | +        class BarData  | 
|---|
 | 185 | +        {  | 
|---|
 | 186 | +  | 
|---|
 | 187 | +                Object thing;  | 
|---|
 | 188 | +                double num;  | 
|---|
 | 189 | +  | 
|---|
 | 190 | +                BarData(Object thing, double num)  | 
|---|
 | 191 | +                {  | 
|---|
 | 192 | +                        this.thing = thing;  | 
|---|
 | 193 | +                        this.num = num;  | 
|---|
 | 194 | +                }  | 
|---|
 | 195 | +        }  | 
|---|
 | 196 | +  | 
|---|
 | 197 | +        class BarGraph extends JPanel  | 
|---|
 | 198 | +        {  | 
|---|
 | 199 | +  | 
|---|
 | 200 | +                int numVals = 0;  | 
|---|
 | 201 | +                int rowHeight = 30;  | 
|---|
 | 202 | +                int barHeight = 20;  | 
|---|
 | 203 | +                int labelX = 10, barLeft = 300, barRight;  | 
|---|
 | 204 | +                int topY = 45;  | 
|---|
 | 205 | +                int numX = 210;  | 
|---|
 | 206 | +                Field splitField, valueField;  | 
|---|
 | 207 | +                String lastValueMenuChoice;  | 
|---|
 | 208 | +                double min, max;  | 
|---|
 | 209 | +  | 
|---|
 | 210 | +                void redo()  | 
|---|
 | 211 | +                {  | 
|---|
 | 212 | +                        bars = new ArrayList<BarData>();  | 
|---|
 | 213 | +                        splitField = getModel().getDB().getField((String) splitFieldChoice.getSelectedItem());  | 
|---|
 | 214 | +                        if (splitField != null)  | 
|---|
 | 215 | +                        {  | 
|---|
 | 216 | +                                int n = numFieldChoice.getSelectedIndex();  | 
|---|
 | 217 | +  | 
|---|
 | 218 | +                                if (n == 0)  | 
|---|
 | 219 | +                                {  | 
|---|
 | 220 | +                                        agg = Aggregate.COUNT;  | 
|---|
 | 221 | +                                } else  | 
|---|
 | 222 | +                                {  | 
|---|
 | 223 | +                                        agg = n % 2 == 1 ? Aggregate.TOTAL : Aggregate.AVERAGE;  | 
|---|
 | 224 | +                                }  | 
|---|
 | 225 | +  | 
|---|
 | 226 | +                                if (agg == Aggregate.COUNT)  | 
|---|
 | 227 | +                                {  | 
|---|
 | 228 | +                                        Bag<String> bag = DBUtils.countValues(getModel().getActs(), splitField);  | 
|---|
 | 229 | +                                        for (String s : bag.list())  | 
|---|
 | 230 | +                                        {  | 
|---|
 | 231 | +                                                bars.add(new BarData(s, bag.num(s)));  | 
|---|
 | 232 | +                                        }  | 
|---|
 | 233 | +                                } else  | 
|---|
 | 234 | +                                {  | 
|---|
 | 235 | +                                        lastValueMenuChoice = (String) numFieldChoice.getSelectedItem();  | 
|---|
 | 236 | +                                        int colon = lastValueMenuChoice.indexOf(':');  | 
|---|
 | 237 | +                                        valueField = getModel().getDB().getField(lastValueMenuChoice.substring(colon + 2));  | 
|---|
 | 238 | +                                        DoubleBag<String> bag = new DoubleBag<String>();  | 
|---|
 | 239 | +                                        for (Act a : getModel().getActs())  | 
|---|
 | 240 | +                                        {  | 
|---|
 | 241 | +                                                if (splitField.getType() == String.class)  | 
|---|
 | 242 | +                                                {  | 
|---|
 | 243 | +                                                        bag.add(a.getString(splitField), a.getValue(valueField));  | 
|---|
 | 244 | +                                                } else  | 
|---|
 | 245 | +                                                {  | 
|---|
 | 246 | +                                                        String[] tags = a.getTextList(splitField);  | 
|---|
 | 247 | +                                                        for (String tag : tags)  | 
|---|
 | 248 | +                                                        {  | 
|---|
 | 249 | +                                                                bag.add(tag, a.getValue(valueField));  | 
|---|
 | 250 | +                                                        }  | 
|---|
 | 251 | +                                                }  | 
|---|
 | 252 | +                                        }  | 
|---|
 | 253 | +                                        boolean isSum = agg == Aggregate.TOTAL;  | 
|---|
 | 254 | +                                        for (String s : bag.list(isSum))  | 
|---|
 | 255 | +                                        {  | 
|---|
 | 256 | +                                                bars.add(new BarData(s, isSum ? bag.num(s) : bag.average(s)));  | 
|---|
 | 257 | +                                        }  | 
|---|
 | 258 | +                                }  | 
|---|
 | 259 | +                        }  | 
|---|
 | 260 | +                        revalidate();  | 
|---|
 | 261 | +                        repaint();  | 
|---|
 | 262 | +                }  | 
|---|
 | 263 | +  | 
|---|
 | 264 | +                public void paintComponent(Graphics g1)  | 
|---|
 | 265 | +                {  | 
|---|
 | 266 | +                        Graphics2D g = (Graphics2D) g1;  | 
|---|
 | 267 | +                        int w = getSize().width, h = getSize().height;  | 
|---|
 | 268 | +                        g.setColor(Color.white);  | 
|---|
 | 269 | +                        g.fillRect(0, 0, w, h);  | 
|---|
 | 270 | +                        TFModel model = getModel();  | 
|---|
 | 271 | +                        Display display = model.getDisplay();  | 
|---|
 | 272 | +  | 
|---|
 | 273 | +                        if (display.emptyMessage(g, model))  | 
|---|
 | 274 | +                        {  | 
|---|
 | 275 | +                                return;  | 
|---|
 | 276 | +                        }  | 
|---|
 | 277 | +  | 
|---|
 | 278 | +                        if (bars == null)  | 
|---|
 | 279 | +                        {  | 
|---|
 | 280 | +                                return;  | 
|---|
 | 281 | +                        }  | 
|---|
 | 282 | +  | 
|---|
 | 283 | +                        if (bars.size() == 0)  | 
|---|
 | 284 | +                        {  | 
|---|
 | 285 | +                                g.setColor(Color.gray);  | 
|---|
 | 286 | +                                g.drawString("(No data selected.)", 10, 30);  | 
|---|
 | 287 | +                                return;  | 
|---|
 | 288 | +                        }  | 
|---|
 | 289 | +  | 
|---|
 | 290 | +                        int n = bars.size();  | 
|---|
 | 291 | +                        max = bars.get(0).num;  | 
|---|
 | 292 | +                        min = Math.min(0, bars.get(n - 1).num);  | 
|---|
 | 293 | +  | 
|---|
 | 294 | +  | 
|---|
 | 295 | +                        barRight = w - 30;  | 
|---|
 | 296 | +  | 
|---|
 | 297 | +                        int zero = scaleX(0);  | 
|---|
 | 298 | +                        boolean isInColor = (splitField != null && getModel().getColorField() == splitField);  | 
|---|
 | 299 | +  | 
|---|
 | 300 | +                        // draw header  | 
|---|
 | 301 | +                        int titleY = topY - 15;  | 
|---|
 | 302 | +                        g.setColor(Color.black);  | 
|---|
 | 303 | +                        g.setFont(display.big());  | 
|---|
 | 304 | +                        g.drawString(splitField.getName().toUpperCase(), labelX, titleY);  | 
|---|
 | 305 | +                        String aggLabel = agg.toString();  | 
|---|
 | 306 | +                        if (agg != Aggregate.COUNT)  | 
|---|
 | 307 | +                        {  | 
|---|
 | 308 | +                                aggLabel += " " + valueField.getName().toUpperCase();  | 
|---|
 | 309 | +                        }  | 
|---|
 | 310 | +                        g.drawString(aggLabel, barLeft, titleY);  | 
|---|
 | 311 | +                        g.setFont(display.plain());  | 
|---|
 | 312 | +                        FontMetrics fm = display.plainFontMetrics();  | 
|---|
 | 313 | +                        // draw bars  | 
|---|
 | 314 | +  | 
|---|
 | 315 | +                        for (int i = 0; i < n; i++)  | 
|---|
 | 316 | +                        {  | 
|---|
 | 317 | +                                int y = topY + i * rowHeight;  | 
|---|
 | 318 | +                                int ty = y + barHeight;  | 
|---|
 | 319 | +                                BarData data = bars.get(i);  | 
|---|
 | 320 | +  | 
|---|
 | 321 | +                                Color c = null;  | 
|---|
 | 322 | +  | 
|---|
 | 323 | +                                g.setColor(Color.gray);  | 
|---|
 | 324 | +  | 
|---|
 | 325 | +                                // label value  | 
|---|
 | 326 | +                                boolean missing = data.thing == null || (data.thing.toString().length() == 0);  | 
|---|
 | 327 | +                                String label = missing ? "[missing]"  | 
|---|
 | 328 | +                                        : display.format(display.toString(data.thing), 25, false);  | 
|---|
 | 329 | +                                if (isInColor)  | 
|---|
 | 330 | +                                {  | 
|---|
 | 331 | +                                        g.setColor(missing ? Color.gray : display.makeColor(data.thing.toString()));  | 
|---|
 | 332 | +                                        display.makeColor(label);  | 
|---|
 | 333 | +                                }  | 
|---|
 | 334 | +                                g.drawString(label, labelX, ty);  | 
|---|
 | 335 | +  | 
|---|
 | 336 | +                                // label number  | 
|---|
 | 337 | +                                String numLabel = display.format(data.num);  | 
|---|
 | 338 | +                                g.drawString(numLabel, numX + 70 - fm.stringWidth(numLabel), ty);  | 
|---|
 | 339 | +  | 
|---|
 | 340 | +                                // draw bar.  | 
|---|
 | 341 | +                                g.setColor(missing ? Color.lightGray : (isInColor ? c : Display.barColor));  | 
|---|
 | 342 | +                                int x = scaleX(data.num);  | 
|---|
 | 343 | +                                int a = Math.min(x, zero);  | 
|---|
 | 344 | +                                int b = Math.max(x, zero);  | 
|---|
 | 345 | +                                g.fillRect(a, y + 5, b - a, barHeight);  | 
|---|
 | 346 | +                        }  | 
|---|
 | 347 | +                }  | 
|---|
 | 348 | +  | 
|---|
 | 349 | +                int scaleX(double x)  | 
|---|
 | 350 | +                {  | 
|---|
 | 351 | +                        if (max == min)  | 
|---|
 | 352 | +                        {  | 
|---|
 | 353 | +                                return barLeft;  | 
|---|
 | 354 | +                        }  | 
|---|
 | 355 | +                        return (int) (barLeft + (barRight - barLeft) * (x - min) / (max - min));  | 
|---|
 | 356 | +                }  | 
|---|
 | 357 | +  | 
|---|
 | 358 | +                public Dimension getPreferredSize()  | 
|---|
 | 359 | +                {  | 
|---|
 | 360 | +                        return new Dimension(400, 100 + rowHeight * (bars == null ? 0 : bars.size()));  | 
|---|
 | 361 | +                }  | 
|---|
 | 362 | +        }  | 
|---|
 | 363 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.views;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.app.ui.ComponentCluster;  | 
|---|
 | 4 | +import timeflow.data.db.*;  | 
|---|
 | 5 | +import timeflow.data.time.Interval;  | 
|---|
 | 6 | +import timeflow.data.time.RoughTime;  | 
|---|
 | 7 | +import timeflow.model.*;  | 
|---|
 | 8 | +import timeflow.vis.*;  | 
|---|
 | 9 | +import timeflow.vis.calendar.*;  | 
|---|
 | 10 | +  | 
|---|
 | 11 | +import javax.swing.*;  | 
|---|
 | 12 | +import java.awt.*;  | 
|---|
 | 13 | +import java.awt.event.ActionEvent;  | 
|---|
 | 14 | +import java.awt.event.ActionListener;  | 
|---|
 | 15 | +import java.awt.event.AdjustmentEvent;  | 
|---|
 | 16 | +import java.awt.event.AdjustmentListener;  | 
|---|
 | 17 | +import java.awt.image.BufferedImage;  | 
|---|
 | 18 | +import java.util.ArrayList;  | 
|---|
 | 19 | +import java.util.Date;  | 
|---|
 | 20 | +  | 
|---|
 | 21 | +public class CalendarView extends AbstractView {  | 
|---|
 | 22 | +  | 
|---|
 | 23 | +	CalendarPanel calendarPanel;  | 
|---|
 | 24 | +	ScrollingCalendar scroller;  | 
|---|
 | 25 | +	CalendarVisuals visuals;  | 
|---|
 | 26 | +	ActDB lastDB;  | 
|---|
 | 27 | +	JPanel controls;  | 
|---|
 | 28 | +	  | 
|---|
 | 29 | +	@Override  | 
|---|
 | 30 | +	public JComponent _getControls()  | 
|---|
 | 31 | +	{  | 
|---|
 | 32 | +		return controls;  | 
|---|
 | 33 | +	}  | 
|---|
 | 34 | +	  | 
|---|
 | 35 | +	public CalendarView(TFModel model)  | 
|---|
 | 36 | +	{  | 
|---|
 | 37 | +		super(model);  | 
|---|
 | 38 | +		calendarPanel=new CalendarPanel(model);  | 
|---|
 | 39 | +		scroller=new ScrollingCalendar();  | 
|---|
 | 40 | +		setLayout(new GridLayout(1,1));  | 
|---|
 | 41 | +		add(scroller);  | 
|---|
 | 42 | +		  | 
|---|
 | 43 | +		controls=new JPanel();  | 
|---|
 | 44 | +		controls.setLayout(new BorderLayout());  | 
|---|
 | 45 | +  | 
|---|
 | 46 | +		ComponentCluster units=new ComponentCluster("Grid");  | 
|---|
 | 47 | +		controls.add(units, BorderLayout.NORTH);  | 
|---|
 | 48 | +		  | 
|---|
 | 49 | +		ButtonGroup unitGroup=new ButtonGroup();  | 
|---|
 | 50 | +		  | 
|---|
 | 51 | +		JRadioButton days=new JRadioButton(new ImageIcon("images/button_days.gif"),true);  | 
|---|
 | 52 | +		days.setSelectedIcon(new ImageIcon("images/button_days_selected.gif"));  | 
|---|
 | 53 | +		units.addContent(days);  | 
|---|
 | 54 | +		days.addActionListener(new LayoutSetter(CalendarVisuals.Layout.DAY));  | 
|---|
 | 55 | +		unitGroup.add(days);  | 
|---|
 | 56 | +  | 
|---|
 | 57 | +		JRadioButton months=new JRadioButton(new ImageIcon("images/button_months.gif"),false);  | 
|---|
 | 58 | +		months.setSelectedIcon(new ImageIcon("images/button_months_selected.gif"));  | 
|---|
 | 59 | +		units.addContent(months);  | 
|---|
 | 60 | +		months.addActionListener(new LayoutSetter(CalendarVisuals.Layout.MONTH));  | 
|---|
 | 61 | +		unitGroup.add(months);  | 
|---|
 | 62 | +		  | 
|---|
 | 63 | +		JRadioButton years=new JRadioButton(new ImageIcon("images/button_years.gif"),false);  | 
|---|
 | 64 | +		years.setSelectedIcon(new ImageIcon("images/button_years_selected.gif"));  | 
|---|
 | 65 | +		units.addContent(years);  | 
|---|
 | 66 | +		years.addActionListener(new LayoutSetter(CalendarVisuals.Layout.YEAR));	  | 
|---|
 | 67 | +		unitGroup.add(years);  | 
|---|
 | 68 | +		  | 
|---|
 | 69 | +		  | 
|---|
 | 70 | +		ComponentCluster showCluster=new ComponentCluster("Show");  | 
|---|
 | 71 | +		controls.add(showCluster, BorderLayout.CENTER);  | 
|---|
 | 72 | +		  | 
|---|
 | 73 | +		ButtonGroup group=new ButtonGroup();  | 
|---|
 | 74 | +		JRadioButton icon=new JRadioButton(new ImageIcon("images/button_dots.gif"),true);  | 
|---|
 | 75 | +		icon.setSelectedIcon(new ImageIcon("images/button_dots_selected.gif"));  | 
|---|
 | 76 | +		showCluster.addContent(icon);  | 
|---|
 | 77 | +		icon.addActionListener(new DrawStyleSetter(CalendarVisuals.DrawStyle.ICON));  | 
|---|
 | 78 | +		group.add(icon);  | 
|---|
 | 79 | +		  | 
|---|
 | 80 | +		JRadioButton label=new JRadioButton(new ImageIcon("images/button_labels.gif"),false);  | 
|---|
 | 81 | +		label.setSelectedIcon(new ImageIcon("images/button_labels_selected.gif"));  | 
|---|
 | 82 | +		showCluster.addContent(label);  | 
|---|
 | 83 | +		label.addActionListener(new DrawStyleSetter(CalendarVisuals.DrawStyle.LABEL));						  | 
|---|
 | 84 | +		group.add(label);  | 
|---|
 | 85 | +  | 
|---|
 | 86 | +		ComponentCluster layout=new ComponentCluster("Layout");  | 
|---|
 | 87 | +		controls.add(layout, BorderLayout.SOUTH);  | 
|---|
 | 88 | +		  | 
|---|
 | 89 | +		ButtonGroup layoutGroup=new ButtonGroup();  | 
|---|
 | 90 | +		JRadioButton loose=new JRadioButton(new ImageIcon("images/button_expanded.gif"),true);  | 
|---|
 | 91 | +		loose.setSelectedIcon(new ImageIcon("images/button_expanded_selected.gif"));  | 
|---|
 | 92 | +		layout.addContent(loose);  | 
|---|
 | 93 | +		loose.addActionListener(new FitStyleSetter(CalendarVisuals.FitStyle.LOOSE));  | 
|---|
 | 94 | +		layoutGroup.add(loose);  | 
|---|
 | 95 | +		  | 
|---|
 | 96 | +		JRadioButton tight=new JRadioButton(new ImageIcon("images/button_compressed.gif"),false);  | 
|---|
 | 97 | +		tight.setSelectedIcon(new ImageIcon("images/button_compressed_selected.gif"));  | 
|---|
 | 98 | +		layout.addContent(tight);  | 
|---|
 | 99 | +		tight.addActionListener(new FitStyleSetter(CalendarVisuals.FitStyle.TIGHT));  | 
|---|
 | 100 | +		layoutGroup.add(tight);  | 
|---|
 | 101 | +	}  | 
|---|
 | 102 | +	  | 
|---|
 | 103 | +	class LayoutSetter implements ActionListener  | 
|---|
 | 104 | +	{  | 
|---|
 | 105 | +		CalendarVisuals.Layout layout;  | 
|---|
 | 106 | +		LayoutSetter(CalendarVisuals.Layout layout)  | 
|---|
 | 107 | +		{  | 
|---|
 | 108 | +			this.layout=layout;  | 
|---|
 | 109 | +		}  | 
|---|
 | 110 | +		@Override  | 
|---|
 | 111 | +		public void actionPerformed(ActionEvent e) {  | 
|---|
 | 112 | +			setLayoutStyle(layout);  | 
|---|
 | 113 | +		}		  | 
|---|
 | 114 | +	}  | 
|---|
 | 115 | +	  | 
|---|
 | 116 | +	class DrawStyleSetter implements ActionListener  | 
|---|
 | 117 | +	{  | 
|---|
 | 118 | +		CalendarVisuals.DrawStyle style;  | 
|---|
 | 119 | +		DrawStyleSetter(CalendarVisuals.DrawStyle style)  | 
|---|
 | 120 | +		{  | 
|---|
 | 121 | +			this.style=style;  | 
|---|
 | 122 | +		}  | 
|---|
 | 123 | +		@Override  | 
|---|
 | 124 | +		public void actionPerformed(ActionEvent e) {  | 
|---|
 | 125 | +			setDrawStyle(style);  | 
|---|
 | 126 | +		}		  | 
|---|
 | 127 | +	}  | 
|---|
 | 128 | +	  | 
|---|
 | 129 | +	class FitStyleSetter implements ActionListener  | 
|---|
 | 130 | +	{  | 
|---|
 | 131 | +		CalendarVisuals.FitStyle style;  | 
|---|
 | 132 | +		FitStyleSetter(CalendarVisuals.FitStyle style)  | 
|---|
 | 133 | +		{  | 
|---|
 | 134 | +			this.style=style;  | 
|---|
 | 135 | +		}  | 
|---|
 | 136 | +		@Override  | 
|---|
 | 137 | +		public void actionPerformed(ActionEvent e) {  | 
|---|
 | 138 | +			setFitStyle(style);  | 
|---|
 | 139 | +		}		  | 
|---|
 | 140 | +	}  | 
|---|
 | 141 | +	  | 
|---|
 | 142 | +	@Override  | 
|---|
 | 143 | +	public String getName() {  | 
|---|
 | 144 | +		return "Calendar";  | 
|---|
 | 145 | +	}  | 
|---|
 | 146 | +  | 
|---|
 | 147 | +	private void redraw(boolean fresh)  | 
|---|
 | 148 | +	{  | 
|---|
 | 149 | +		visuals.makeGrid(fresh);  | 
|---|
 | 150 | +		calendarPanel.drawVisualization();  | 
|---|
 | 151 | +		repaint();  | 
|---|
 | 152 | +	}  | 
|---|
 | 153 | +  | 
|---|
 | 154 | +	void setLayoutStyle(CalendarVisuals.Layout layout)  | 
|---|
 | 155 | +	{  | 
|---|
 | 156 | +		visuals.setLayoutStyle(layout);  | 
|---|
 | 157 | +		calendarPanel.drawVisualization();  | 
|---|
 | 158 | +		revalidate();  | 
|---|
 | 159 | +		repaint();  | 
|---|
 | 160 | +	}  | 
|---|
 | 161 | +  | 
|---|
 | 162 | +	void setDrawStyle(CalendarVisuals.DrawStyle style)  | 
|---|
 | 163 | +	{  | 
|---|
 | 164 | +		visuals.setDrawStyle(style);  | 
|---|
 | 165 | +		calendarPanel.drawVisualization();  | 
|---|
 | 166 | +		revalidate();  | 
|---|
 | 167 | +		repaint();  | 
|---|
 | 168 | +	}  | 
|---|
 | 169 | +  | 
|---|
 | 170 | +	void setFitStyle(CalendarVisuals.FitStyle style)  | 
|---|
 | 171 | +	{  | 
|---|
 | 172 | +		visuals.setFitStyle(style);  | 
|---|
 | 173 | +		calendarPanel.drawVisualization();  | 
|---|
 | 174 | +		revalidate();  | 
|---|
 | 175 | +		repaint();  | 
|---|
 | 176 | +	}  | 
|---|
 | 177 | +	  | 
|---|
 | 178 | +	@Override  | 
|---|
 | 179 | +	protected void onscreen(boolean majorChange)  | 
|---|
 | 180 | +	{  | 
|---|
 | 181 | +		visuals.initAllButGrid();  | 
|---|
 | 182 | +		scroller.calibrate(true);  | 
|---|
 | 183 | +		revalidate();  | 
|---|
 | 184 | +		ActDB db=getModel().getDB();  | 
|---|
 | 185 | +		redraw(majorChange);  | 
|---|
 | 186 | +		scroller.calibrate(majorChange);  | 
|---|
 | 187 | +		lastDB=db;  | 
|---|
 | 188 | +	}  | 
|---|
 | 189 | +	  | 
|---|
 | 190 | +	@Override  | 
|---|
 | 191 | +	protected void _note(TFEvent e) {  | 
|---|
 | 192 | +		int oldHeight=calendarPanel.getPreferredSize().height;  | 
|---|
 | 193 | +		visuals.note(e);  | 
|---|
 | 194 | +		  | 
|---|
 | 195 | +		calendarPanel.drawVisualization();  | 
|---|
 | 196 | +		calendarPanel.repaint();  | 
|---|
 | 197 | +		if (e.affectsData() || oldHeight!=calendarPanel.getPreferredSize().height)  | 
|---|
 | 198 | +		{  | 
|---|
 | 199 | +			SwingUtilities.invokeLater(new Runnable() {  | 
|---|
 | 200 | +				public void run() {scroller.calibrate(false); revalidate();}  | 
|---|
 | 201 | +			});  | 
|---|
 | 202 | +		}  | 
|---|
 | 203 | +		revalidate();  | 
|---|
 | 204 | +	}  | 
|---|
 | 205 | +	  | 
|---|
 | 206 | +	public void setBounds(int x, int y, int w, int h)  | 
|---|
 | 207 | +	{  | 
|---|
 | 208 | +		super.setBounds(x,y,w,h);  | 
|---|
 | 209 | +		if (visuals==null || visuals.grid==null)  | 
|---|
 | 210 | +			return;  | 
|---|
 | 211 | +		calendarPanel.drawVisualization();  | 
|---|
 | 212 | +		calendarPanel.repaint();  | 
|---|
 | 213 | +	}  | 
|---|
 | 214 | +	  | 
|---|
 | 215 | +	class ScrollingCalendar extends JPanel  | 
|---|
 | 216 | +	{  | 
|---|
 | 217 | +		JScrollBar bar;  | 
|---|
 | 218 | +		public ScrollingCalendar()  | 
|---|
 | 219 | +		{  | 
|---|
 | 220 | +			setLayout(new BorderLayout());  | 
|---|
 | 221 | +			add(calendarPanel, BorderLayout.CENTER);  | 
|---|
 | 222 | +			bar=new JScrollBar(JScrollBar.VERTICAL);  | 
|---|
 | 223 | +			add(bar, BorderLayout.EAST);  | 
|---|
 | 224 | +			bar.addAdjustmentListener(new AdjustmentListener() {			  | 
|---|
 | 225 | +				@Override  | 
|---|
 | 226 | +				public void adjustmentValueChanged(AdjustmentEvent e) {  | 
|---|
 | 227 | +					visuals.grid.setDY(bar.getValue());  | 
|---|
 | 228 | +					  | 
|---|
 | 229 | +					// set time in model.  | 
|---|
 | 230 | +					RoughTime startTime=visuals.grid.getFirstDrawnTime();  | 
|---|
 | 231 | +					Interval viewInterval=getModel().getViewInterval();  | 
|---|
 | 232 | +					if (viewInterval!=null)  | 
|---|
 | 233 | +					{  | 
|---|
 | 234 | +						viewInterval.translateTo(startTime.getTime());  | 
|---|
 | 235 | +					}  | 
|---|
 | 236 | +					  | 
|---|
 | 237 | +					calendarPanel.drawVisualization();  | 
|---|
 | 238 | +					calendarPanel.repaint();  | 
|---|
 | 239 | +				}  | 
|---|
 | 240 | +			});  | 
|---|
 | 241 | +		}  | 
|---|
 | 242 | +		  | 
|---|
 | 243 | +		public void setBounds(int x, int y, int w, int h)  | 
|---|
 | 244 | +		{  | 
|---|
 | 245 | +			if (x==getX() && y==getY() && w==getWidth() && h==getHeight())  | 
|---|
 | 246 | +				return;  | 
|---|
 | 247 | +			super.setBounds(x,y,w,h);  | 
|---|
 | 248 | +			calibrate(false);  | 
|---|
 | 249 | +		}  | 
|---|
 | 250 | +		  | 
|---|
 | 251 | +		void calibrate(boolean forceValue)  | 
|---|
 | 252 | +		{  | 
|---|
 | 253 | +			if (visuals==null || visuals.grid==null)  | 
|---|
 | 254 | +				return;  | 
|---|
 | 255 | +			int height=getSize().height;  | 
|---|
 | 256 | +			int desired=visuals.grid.getCalendarHeight();  | 
|---|
 | 257 | +			bar.setVisible(desired>height);  | 
|---|
 | 258 | +			if (desired>height)  | 
|---|
 | 259 | +			{  | 
|---|
 | 260 | +				bar.setMinimum(0);  | 
|---|
 | 261 | +				bar.setMaximum(desired);  | 
|---|
 | 262 | +				bar.setVisibleAmount(height);  | 
|---|
 | 263 | +				Interval view=getModel().getViewInterval();  | 
|---|
 | 264 | +				if (view!=null && forceValue)  | 
|---|
 | 265 | +				{					  | 
|---|
 | 266 | +					double s=visuals.grid.getScrollFraction();  | 
|---|
 | 267 | +					double maxFraction=(desired-height)/(double)desired;  | 
|---|
 | 268 | +					int value=(int)((s/maxFraction)*desired);  | 
|---|
 | 269 | +					bar.setValue(value);  | 
|---|
 | 270 | +				}  | 
|---|
 | 271 | +				  | 
|---|
 | 272 | +			}  | 
|---|
 | 273 | +		}  | 
|---|
 | 274 | +	}  | 
|---|
 | 275 | +	  | 
|---|
 | 276 | +	class CalendarPanel extends AbstractVisualizationView  | 
|---|
 | 277 | +	{		  | 
|---|
 | 278 | +  | 
|---|
 | 279 | +		CalendarPanel(TFModel model)  | 
|---|
 | 280 | +		{  | 
|---|
 | 281 | +			super(model);  | 
|---|
 | 282 | +			setBackground(Color.white);  | 
|---|
 | 283 | +			visuals=new CalendarVisuals(getModel());  | 
|---|
 | 284 | +		}	  | 
|---|
 | 285 | +  | 
|---|
 | 286 | +		public RoughTime getTime(Point p)  | 
|---|
 | 287 | +		{  | 
|---|
 | 288 | +			return visuals.grid.getTime(p.x, p.y);   | 
|---|
 | 289 | +		}  | 
|---|
 | 290 | +		  | 
|---|
 | 291 | +		protected void drawVisualization(Graphics2D g)  | 
|---|
 | 292 | +		{  | 
|---|
 | 293 | +			g.setBackground(Color.white);  | 
|---|
 | 294 | +			g.fillRect(0,0,getSize().width, getSize().height);  | 
|---|
 | 295 | +  | 
|---|
 | 296 | +			getModel().getDisplay().emptyMessage(g, model);  | 
|---|
 | 297 | +			if (model.getDB()==null)  | 
|---|
 | 298 | +				return;  | 
|---|
 | 299 | +			visuals.setBounds(0,0,getSize().width,getSize().height);	  | 
|---|
 | 300 | +			objectLocations=new ArrayList<Mouseover>();  | 
|---|
 | 301 | +			visuals.render(g, objectLocations);  | 
|---|
 | 302 | +		}  | 
|---|
 | 303 | +	}  | 
|---|
 | 304 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.views;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.model.*;  | 
|---|
 | 4 | +import timeflow.util.Pad;  | 
|---|
 | 5 | +import timeflow.data.db.*;  | 
|---|
 | 6 | +  | 
|---|
 | 7 | +import java.awt.*;  | 
|---|
 | 8 | +import java.awt.event.*;  | 
|---|
 | 9 | +  | 
|---|
 | 10 | +import javax.swing.*;  | 
|---|
 | 11 | +  | 
|---|
 | 12 | +public class DescriptionView extends AbstractView {  | 
|---|
 | 13 | +  | 
|---|
 | 14 | +	JTextArea content;  | 
|---|
 | 15 | +	JComponent controls;  | 
|---|
 | 16 | +	  | 
|---|
 | 17 | +	public DescriptionView(TFModel model) {  | 
|---|
 | 18 | +		super(model);  | 
|---|
 | 19 | +		setLayout(new BorderLayout());  | 
|---|
 | 20 | +		JPanel left=new Pad(5,5);  | 
|---|
 | 21 | +		left.setBackground(Color.white);  | 
|---|
 | 22 | +		add(left, BorderLayout.WEST);  | 
|---|
 | 23 | +		JPanel right=new Pad(5,5);  | 
|---|
 | 24 | +		right.setBackground(Color.white);  | 
|---|
 | 25 | +		add(right, BorderLayout.EAST);  | 
|---|
 | 26 | +		JPanel top=new JPanel();  | 
|---|
 | 27 | +		add(top, BorderLayout.NORTH);  | 
|---|
 | 28 | +		top.setLayout(new FlowLayout(FlowLayout.LEFT));  | 
|---|
 | 29 | +		top.add(new JLabel("Notes & Comments on This Data:"));  | 
|---|
 | 30 | +		content=new JTextArea();  | 
|---|
 | 31 | +		content.setLineWrap(true);  | 
|---|
 | 32 | +		content.setWrapStyleWord(true);  | 
|---|
 | 33 | +		add(content, BorderLayout.CENTER);  | 
|---|
 | 34 | +		content.addKeyListener(new KeyAdapter() {  | 
|---|
 | 35 | +			@Override  | 
|---|
 | 36 | +			public void keyReleased(KeyEvent e) {  | 
|---|
 | 37 | +				getModel().getDB().setDescription(content.getText());  | 
|---|
 | 38 | +				getModel().noteNewDescription(DescriptionView.this);  | 
|---|
 | 39 | +			}});  | 
|---|
 | 40 | +		controls=new HtmlControls("Each TimeFlow data set<br> comes with a free-form <br> "+  | 
|---|
 | 41 | +				"description area. <p>This is a good place to write<br> notes "+  | 
|---|
 | 42 | +				"about sources, how the data<br>was cleaned, etc.");  | 
|---|
 | 43 | +	}  | 
|---|
 | 44 | +	  | 
|---|
 | 45 | +	@Override  | 
|---|
 | 46 | +	public JComponent _getControls()  | 
|---|
 | 47 | +	{  | 
|---|
 | 48 | +		return controls;  | 
|---|
 | 49 | +	}  | 
|---|
 | 50 | +	  | 
|---|
 | 51 | +	@Override  | 
|---|
 | 52 | +	protected void _note(TFEvent e) {  | 
|---|
 | 53 | +		if (e.type==TFEvent.Type.DESCRIPTION_CHANGE || e.type==TFEvent.Type.DATABASE_CHANGE)  | 
|---|
 | 54 | +		{  | 
|---|
 | 55 | +			content.setText(getModel().getDB().getDescription());  | 
|---|
 | 56 | +			repaint();  | 
|---|
 | 57 | +		}  | 
|---|
 | 58 | +	}  | 
|---|
 | 59 | +  | 
|---|
 | 60 | +	@Override  | 
|---|
 | 61 | +	public String getName() {  | 
|---|
 | 62 | +		return "Notes";  | 
|---|
 | 63 | +	}  | 
|---|
 | 64 | +  | 
|---|
 | 65 | +	@Override  | 
|---|
 | 66 | +	protected void onscreen(boolean majorChange) {  | 
|---|
 | 67 | +		ActDB db=getModel().getDB();  | 
|---|
 | 68 | +		content.setText(db.getDescription());  | 
|---|
 | 69 | +		content.requestFocus();  | 
|---|
 | 70 | +	}  | 
|---|
 | 71 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.views;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import java.awt.Color;  | 
|---|
 | 4 | +import java.awt.GridLayout;  | 
|---|
 | 5 | +  | 
|---|
 | 6 | +import javax.swing.*;  | 
|---|
 | 7 | +  | 
|---|
 | 8 | +import timeflow.app.ui.HtmlDisplay;  | 
|---|
 | 9 | +  | 
|---|
 | 10 | +public class HtmlControls extends JPanel {  | 
|---|
 | 11 | +	  | 
|---|
 | 12 | +	public HtmlControls(String text)  | 
|---|
 | 13 | +	{  | 
|---|
 | 14 | +		JEditorPane html=HtmlDisplay.create();  | 
|---|
 | 15 | +		html.setText(text);  | 
|---|
 | 16 | +		setBackground(Color.white);  | 
|---|
 | 17 | +		setBorder(BorderFactory.createEmptyBorder(10,10,10,10));  | 
|---|
 | 18 | +		setLayout(new GridLayout(1,1));  | 
|---|
 | 19 | +		add(html);  | 
|---|
 | 20 | +	}  | 
|---|
 | 21 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.views;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.app.ui.EditRecordPanel;  | 
|---|
 | 4 | +import timeflow.data.analysis.*;  | 
|---|
 | 5 | +import timeflow.data.db.*;  | 
|---|
 | 6 | +import timeflow.data.time.*;  | 
|---|
 | 7 | +import timeflow.format.field.FieldFormatCatalog;  | 
|---|
 | 8 | +import timeflow.model.*;  | 
|---|
 | 9 | +import timeflow.views.*;  | 
|---|
 | 10 | +import timeflow.views.ListView.LinkIt;  | 
|---|
 | 11 | +  | 
|---|
 | 12 | +import java.awt.*;  | 
|---|
 | 13 | +import java.io.File;  | 
|---|
 | 14 | +import java.net.URL;  | 
|---|
 | 15 | +import java.util.Date;  | 
|---|
 | 16 | +  | 
|---|
 | 17 | +import javax.imageio.ImageIO;  | 
|---|
 | 18 | +import javax.swing.JComponent;  | 
|---|
 | 19 | +import javax.swing.JEditorPane;  | 
|---|
 | 20 | +import javax.swing.JScrollPane;  | 
|---|
 | 21 | +import javax.swing.UIManager;  | 
|---|
 | 22 | +import javax.swing.event.HyperlinkEvent;  | 
|---|
 | 23 | +import javax.swing.event.HyperlinkListener;  | 
|---|
 | 24 | +import javax.swing.table.TableModel;  | 
|---|
 | 25 | +import javax.swing.text.html.HTMLDocument;  | 
|---|
 | 26 | +import javax.swing.text.html.StyleSheet;  | 
|---|
 | 27 | +  | 
|---|
 | 28 | +import timeflow.util.*;  | 
|---|
 | 29 | +  | 
|---|
 | 30 | +public class IntroView extends AbstractView  | 
|---|
 | 31 | +{  | 
|---|
 | 32 | +  | 
|---|
 | 33 | +        private JComponent controls;  | 
|---|
 | 34 | +        Image image;  | 
|---|
 | 35 | +        Image repeat;  | 
|---|
 | 36 | +  | 
|---|
 | 37 | +        public IntroView(TFModel model)  | 
|---|
 | 38 | +        {  | 
|---|
 | 39 | +                super(model);  | 
|---|
 | 40 | +                setBackground(Color.white);  | 
|---|
 | 41 | +                try  | 
|---|
 | 42 | +                {  | 
|---|
 | 43 | +                        image = ImageIO.read(new File("images/intro.gif"));  | 
|---|
 | 44 | +                        repeat = ImageIO.read(new File("images/repeat.gif"));  | 
|---|
 | 45 | +                } catch (Exception e)  | 
|---|
 | 46 | +                {  | 
|---|
 | 47 | +                        System.out.println("Couldn't load images.");  | 
|---|
 | 48 | +                        e.printStackTrace(System.out);  | 
|---|
 | 49 | +                }  | 
|---|
 | 50 | +                makeHtml();  | 
|---|
 | 51 | +        }  | 
|---|
 | 52 | +  | 
|---|
 | 53 | +        public void paintComponent(Graphics g)  | 
|---|
 | 54 | +        {  | 
|---|
 | 55 | +                g.setColor(Color.white);  | 
|---|
 | 56 | +                int w = getSize().width, h = getSize().height;  | 
|---|
 | 57 | +                g.fillRect(0, 0, w, h);  | 
|---|
 | 58 | +                // draw image and extensible background, so it looks cool on a big screen.  | 
|---|
 | 59 | +                if (image != null && repeat != null)  | 
|---|
 | 60 | +                {  | 
|---|
 | 61 | +                        int ih = image.getHeight(null);  | 
|---|
 | 62 | +                        int iw = image.getWidth(null);  | 
|---|
 | 63 | +                        int rw = repeat.getWidth(null);  | 
|---|
 | 64 | +                        g.drawImage(image, 0, 0, null);  | 
|---|
 | 65 | +                        for (int x = iw; x < w; x += rw)  | 
|---|
 | 66 | +                        {  | 
|---|
 | 67 | +                                g.drawImage(repeat, x, 0, null);  | 
|---|
 | 68 | +                        }  | 
|---|
 | 69 | +                }  | 
|---|
 | 70 | +        }  | 
|---|
 | 71 | +  | 
|---|
 | 72 | +        void makeHtml()  | 
|---|
 | 73 | +        {  | 
|---|
 | 74 | +                try  | 
|---|
 | 75 | +                {  | 
|---|
 | 76 | +                        String sidebar = IO.read("settings/sidebar.html");  | 
|---|
 | 77 | +                        controls = new HtmlControls(sidebar);  | 
|---|
 | 78 | +                } catch (Exception e)  | 
|---|
 | 79 | +                {  | 
|---|
 | 80 | +                        e.printStackTrace(System.out);  | 
|---|
 | 81 | +                }  | 
|---|
 | 82 | +        }  | 
|---|
 | 83 | +  | 
|---|
 | 84 | +        @Override  | 
|---|
 | 85 | +        public JComponent _getControls()  | 
|---|
 | 86 | +        {  | 
|---|
 | 87 | +                return controls;  | 
|---|
 | 88 | +        }  | 
|---|
 | 89 | +  | 
|---|
 | 90 | +        @Override  | 
|---|
 | 91 | +        protected void onscreen(boolean majorChange)  | 
|---|
 | 92 | +        {  | 
|---|
 | 93 | +        }  | 
|---|
 | 94 | +  | 
|---|
 | 95 | +        protected void _note(TFEvent e)  | 
|---|
 | 96 | +        {  | 
|---|
 | 97 | +                // do nothing.  | 
|---|
 | 98 | +        }  | 
|---|
 | 99 | +  | 
|---|
 | 100 | +        @Override  | 
|---|
 | 101 | +        public String getName()  | 
|---|
 | 102 | +        {  | 
|---|
 | 103 | +                return "About";  | 
|---|
 | 104 | +        }  | 
|---|
 | 105 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.views;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.app.ui.EditRecordPanel;  | 
|---|
 | 4 | +import timeflow.app.ui.HtmlDisplay;  | 
|---|
 | 5 | +import timeflow.data.db.*;  | 
|---|
 | 6 | +import timeflow.format.file.HtmlFormat;  | 
|---|
 | 7 | +import timeflow.model.*;  | 
|---|
 | 8 | +  | 
|---|
 | 9 | +import java.awt.*;  | 
|---|
 | 10 | +import java.awt.event.*;  | 
|---|
 | 11 | +  | 
|---|
 | 12 | +import javax.swing.*;  | 
|---|
 | 13 | +import javax.swing.event.*;  | 
|---|
 | 14 | +import javax.swing.table.*;  | 
|---|
 | 15 | +import javax.swing.text.html.*;  | 
|---|
 | 16 | +  | 
|---|
 | 17 | +import timeflow.util.*;  | 
|---|
 | 18 | +  | 
|---|
 | 19 | +import java.net.URI;  | 
|---|
 | 20 | +import java.net.URL;  | 
|---|
 | 21 | +import java.util.*;  | 
|---|
 | 22 | +  | 
|---|
 | 23 | +public class ListView extends AbstractView {  | 
|---|
 | 24 | +  | 
|---|
 | 25 | +	private JEditorPane listDisplay;  | 
|---|
 | 26 | +	private JComboBox sortMenu=new JComboBox();  | 
|---|
 | 27 | +	private ActComparator sort;//=ActComparator.byTime();  | 
|---|
 | 28 | +	private int maxPerPage=50;  | 
|---|
 | 29 | +	private int pageStart=0;  | 
|---|
 | 30 | +	private int lastSize=0;  | 
|---|
 | 31 | +	private ActList acts;  | 
|---|
 | 32 | +	private Field sortField;  | 
|---|
 | 33 | +	  | 
|---|
 | 34 | +	private JLabel pageLabel=new JLabel("Page", JLabel.LEFT);  | 
|---|
 | 35 | +	private JComboBox pageMenu=new JComboBox();  | 
|---|
 | 36 | +	private boolean changing=false;  | 
|---|
 | 37 | +	  | 
|---|
 | 38 | +	private JPanel controls;  | 
|---|
 | 39 | +	  | 
|---|
 | 40 | +	public ListView(TFModel model)  | 
|---|
 | 41 | +	{  | 
|---|
 | 42 | +		super(model);  | 
|---|
 | 43 | +		  | 
|---|
 | 44 | +		listDisplay=HtmlDisplay.create();  | 
|---|
 | 45 | +		listDisplay.addHyperlinkListener(new LinkIt());		  | 
|---|
 | 46 | +		JScrollPane scrollPane = new JScrollPane(listDisplay);  | 
|---|
 | 47 | +		setLayout(new BorderLayout());  | 
|---|
 | 48 | +		add(scrollPane, BorderLayout.CENTER);  | 
|---|
 | 49 | +		  | 
|---|
 | 50 | +		  | 
|---|
 | 51 | +		controls=new JPanel();  | 
|---|
 | 52 | +		controls.setLayout(null);  | 
|---|
 | 53 | +		controls.setBackground(Color.white);  | 
|---|
 | 54 | +		  | 
|---|
 | 55 | +		int x=10, y=10;  | 
|---|
 | 56 | +		int ch=25, pad=5, cw=160;  | 
|---|
 | 57 | +		JLabel sortLabel=new JLabel("Sort Order", JLabel.LEFT);  | 
|---|
 | 58 | +		controls.add(sortLabel);  | 
|---|
 | 59 | +		sortLabel.setBounds(x,y,cw,ch);  | 
|---|
 | 60 | +		y+=ch+pad;  | 
|---|
 | 61 | +		  | 
|---|
 | 62 | +		controls.add(sortMenu);	  | 
|---|
 | 63 | +		sortMenu.setBounds(x,y,cw,ch);  | 
|---|
 | 64 | +		y+=ch+3*pad;  | 
|---|
 | 65 | +		  | 
|---|
 | 66 | +		controls.add(pageLabel);  | 
|---|
 | 67 | +		pageLabel.setBounds(x,y,cw,ch);  | 
|---|
 | 68 | +		y+=ch+pad;  | 
|---|
 | 69 | +		controls.add(pageMenu);  | 
|---|
 | 70 | +		pageMenu.setBounds(x,y,cw,ch);  | 
|---|
 | 71 | +		  | 
|---|
 | 72 | +		showPageMenu(false);  | 
|---|
 | 73 | +		pageMenu.addActionListener(pageListener);				  | 
|---|
 | 74 | +		sortMenu.addActionListener(sortListener);  | 
|---|
 | 75 | +	}  | 
|---|
 | 76 | +	  | 
|---|
 | 77 | +	protected JComponent _getControls()  | 
|---|
 | 78 | +	{  | 
|---|
 | 79 | +		return controls;  | 
|---|
 | 80 | +	}  | 
|---|
 | 81 | +	  | 
|---|
 | 82 | +	ActionListener sortListener=new ActionListener() {  | 
|---|
 | 83 | +		@Override  | 
|---|
 | 84 | +		public void actionPerformed(ActionEvent e) {  | 
|---|
 | 85 | +			if (changing || sortMenu.getItemCount()<=0) // this means the action was fired after all items removed.  | 
|---|
 | 86 | +				return;  | 
|---|
 | 87 | +			sortField=getModel().getDB().getField((String)sortMenu.getSelectedItem());  | 
|---|
 | 88 | +			sort=sortField==null ? null : ActComparator.by(sortField);  | 
|---|
 | 89 | +			setToFirstPage();  | 
|---|
 | 90 | +			makeList();  | 
|---|
 | 91 | +		}};  | 
|---|
 | 92 | +	  | 
|---|
 | 93 | +	ActionListener pageListener=new ActionListener() {  | 
|---|
 | 94 | +		@Override  | 
|---|
 | 95 | +		public void actionPerformed(ActionEvent e) {  | 
|---|
 | 96 | +			if (changing)  | 
|---|
 | 97 | +				return;  | 
|---|
 | 98 | +			pageStart=maxPerPage*pageMenu.getSelectedIndex();  | 
|---|
 | 99 | +			System.out.println(e.getActionCommand());  | 
|---|
 | 100 | +			makeList();  | 
|---|
 | 101 | +		}};  | 
|---|
 | 102 | +	  | 
|---|
 | 103 | +	@Override  | 
|---|
 | 104 | +	protected void onscreen(boolean majorChange)  | 
|---|
 | 105 | +	{  | 
|---|
 | 106 | +		_note(null);  | 
|---|
 | 107 | +	}  | 
|---|
 | 108 | +	  | 
|---|
 | 109 | +	public void _note(TFEvent e) {  | 
|---|
 | 110 | +		changing=true;  | 
|---|
 | 111 | +		if (e==null || e.affectsSchema() || e.affectsRowSet())  | 
|---|
 | 112 | +		{  | 
|---|
 | 113 | +			sortMenu.removeActionListener(sortListener);  | 
|---|
 | 114 | +			sortMenu.removeAllItems();  | 
|---|
 | 115 | +			pageStart=0;  | 
|---|
 | 116 | +			java.util.List<Field> fields=getModel().getDB().getFields();  | 
|---|
 | 117 | +			Field firstField=null;  | 
|---|
 | 118 | +			if (fields.size()>0)  | 
|---|
 | 119 | +				firstField=fields.get(0);  | 
|---|
 | 120 | +			for (Field f: fields)  | 
|---|
 | 121 | +			{  | 
|---|
 | 122 | +				sortMenu.addItem(f.getName());  | 
|---|
 | 123 | +			}  | 
|---|
 | 124 | +			sortField=getModel().getDB().getField(VirtualField.START);  | 
|---|
 | 125 | +			if (sortField!=null)  | 
|---|
 | 126 | +				sortMenu.setSelectedItem(sortField.getName());  | 
|---|
 | 127 | +			else  | 
|---|
 | 128 | +				sortField=firstField;  | 
|---|
 | 129 | +			sortMenu.addActionListener(sortListener);  | 
|---|
 | 130 | +			sort=null;  | 
|---|
 | 131 | +		}  | 
|---|
 | 132 | +		if (e!=null && e.affectsData())  | 
|---|
 | 133 | +		{  | 
|---|
 | 134 | +			setToFirstPage();  | 
|---|
 | 135 | +		}  | 
|---|
 | 136 | +		changing=false;  | 
|---|
 | 137 | +		makeList();  | 
|---|
 | 138 | +	}  | 
|---|
 | 139 | +	  | 
|---|
 | 140 | +	private void setToFirstPage()  | 
|---|
 | 141 | +	{  | 
|---|
 | 142 | +		pageStart=0;  | 
|---|
 | 143 | +		if (pageMenu.isVisible())  | 
|---|
 | 144 | +		{  | 
|---|
 | 145 | +			pageMenu.removeActionListener(pageListener);  | 
|---|
 | 146 | +			pageMenu.setSelectedIndex(0);  | 
|---|
 | 147 | +			pageMenu.addActionListener(pageListener);  | 
|---|
 | 148 | +		}  | 
|---|
 | 149 | +	}  | 
|---|
 | 150 | +	  | 
|---|
 | 151 | +	void showPageMenu(boolean visible)  | 
|---|
 | 152 | +	{  | 
|---|
 | 153 | +		pageLabel.setVisible(visible);  | 
|---|
 | 154 | +		pageMenu.setVisible(visible);  | 
|---|
 | 155 | +		if (visible)  | 
|---|
 | 156 | +		{  | 
|---|
 | 157 | +			pageMenu.removeActionListener(pageListener);  | 
|---|
 | 158 | +			pageMenu.setSelectedIndex(pageStart/maxPerPage);  | 
|---|
 | 159 | +			pageMenu.addActionListener(pageListener);  | 
|---|
 | 160 | +		}  | 
|---|
 | 161 | +	}  | 
|---|
 | 162 | +	  | 
|---|
 | 163 | +	  | 
|---|
 | 164 | +	void makeList()  | 
|---|
 | 165 | +	{  | 
|---|
 | 166 | +		HtmlFormat html=new HtmlFormat();  | 
|---|
 | 167 | +		html.setModel(getModel());  | 
|---|
 | 168 | +		StringBuffer page=new StringBuffer();  | 
|---|
 | 169 | +		  | 
|---|
 | 170 | +		page.append(html.makeHeader());  | 
|---|
 | 171 | +		  | 
|---|
 | 172 | +		  | 
|---|
 | 173 | +		ActList as=getModel().getActs();  | 
|---|
 | 174 | +		if (as==null || as.size()==0 && getModel().getDB().size()==0)  | 
|---|
 | 175 | +		{  | 
|---|
 | 176 | +			page.append("<tr><td><h1><font color=#003399>Empty Database</font></h1></td></tr>");  | 
|---|
 | 177 | +			showPageMenu(false);  | 
|---|
 | 178 | +		}  | 
|---|
 | 179 | +		else  | 
|---|
 | 180 | +		{  | 
|---|
 | 181 | +			  | 
|---|
 | 182 | +			if (sort==null)  | 
|---|
 | 183 | +			{  | 
|---|
 | 184 | +				Field timeField=getModel().getDB().getField(VirtualField.START);  | 
|---|
 | 185 | +				if (timeField!=null)			  | 
|---|
 | 186 | +					sort=ActComparator.by(timeField);  | 
|---|
 | 187 | +			}  | 
|---|
 | 188 | +  | 
|---|
 | 189 | +			acts=as.copy();  | 
|---|
 | 190 | +			if (sort!=null)  | 
|---|
 | 191 | +				Collections.sort(acts, sort);  | 
|---|
 | 192 | +			  | 
|---|
 | 193 | +			boolean pages=acts.size()>maxPerPage;  | 
|---|
 | 194 | +			int last=Math.min(acts.size(), pageStart+maxPerPage);  | 
|---|
 | 195 | +			if (pages)  | 
|---|
 | 196 | +			{  | 
|---|
 | 197 | +				int n=acts.size();  | 
|---|
 | 198 | +				if (lastSize!=n)  | 
|---|
 | 199 | +				{  | 
|---|
 | 200 | +					pageMenu.removeActionListener(pageListener);  | 
|---|
 | 201 | +					pageMenu.removeAllItems();  | 
|---|
 | 202 | +					for (int i=0; i*maxPerPage<n;i++)  | 
|---|
 | 203 | +					{  | 
|---|
 | 204 | +						pageMenu.addItem("Items "+((i*maxPerPage)+1)+" to "+  | 
|---|
 | 205 | +								Math.min(n, (i+1)*maxPerPage));  | 
|---|
 | 206 | +					}  | 
|---|
 | 207 | +					pageMenu.addActionListener(pageListener);  | 
|---|
 | 208 | +					lastSize=n;  | 
|---|
 | 209 | +				}  | 
|---|
 | 210 | +			}  | 
|---|
 | 211 | +			showPageMenu(pages);  | 
|---|
 | 212 | +			  | 
|---|
 | 213 | +			page.append("<tr><td><h1><font color=#003399>"+(pages? (pageStart+1)+"-"+(last) +" of ": "")+acts.size()+" Events</font></h1>");  | 
|---|
 | 214 | +			page.append("<br><br></td></tr>");  | 
|---|
 | 215 | +  | 
|---|
 | 216 | +			for (int i=pageStart; i<last; i++)  | 
|---|
 | 217 | +			{  | 
|---|
 | 218 | +				Act a=acts.get(i);  | 
|---|
 | 219 | +				page.append(html.makeItem(a,i));  | 
|---|
 | 220 | +			}  | 
|---|
 | 221 | +		}  | 
|---|
 | 222 | +		page.append(html.makeFooter());  | 
|---|
 | 223 | +		listDisplay.setText(page.toString());  | 
|---|
 | 224 | +		listDisplay.setCaretPosition(0);  | 
|---|
 | 225 | +		repaint();  | 
|---|
 | 226 | +	}  | 
|---|
 | 227 | +	  | 
|---|
 | 228 | +	  | 
|---|
 | 229 | +	  | 
|---|
 | 230 | +	  | 
|---|
 | 231 | +	@Override  | 
|---|
 | 232 | +	public String getName() {  | 
|---|
 | 233 | +		return "List";  | 
|---|
 | 234 | +	}  | 
|---|
 | 235 | +	  | 
|---|
 | 236 | +	static class ArrayRenderer extends DefaultTableCellRenderer {  | 
|---|
 | 237 | +	    public void setValue(Object value) {  | 
|---|
 | 238 | +	    	setText(Display.arrayToString((Object[])value));  | 
|---|
 | 239 | +	    }  | 
|---|
 | 240 | +	}  | 
|---|
 | 241 | +	  | 
|---|
 | 242 | +	public class LinkIt implements HyperlinkListener   | 
|---|
 | 243 | +	{  | 
|---|
 | 244 | +		public void hyperlinkUpdate(HyperlinkEvent e)   | 
|---|
 | 245 | +		{  | 
|---|
 | 246 | +			if (e.getEventType() != HyperlinkEvent.EventType.ACTIVATED)   | 
|---|
 | 247 | +				return;  | 
|---|
 | 248 | +			  | 
|---|
 | 249 | +			String s=e.getDescription();  | 
|---|
 | 250 | +			System.out.println(s);  | 
|---|
 | 251 | +			if (s.length()>0)  | 
|---|
 | 252 | +			{  | 
|---|
 | 253 | +				char c=s.charAt(0);  | 
|---|
 | 254 | +				if (c=='e') // code for "edit"  | 
|---|
 | 255 | +				{  | 
|---|
 | 256 | +					int i=Integer.parseInt(s.substring(1));  | 
|---|
 | 257 | +					EditRecordPanel.edit(getModel(), acts.get(i));  | 
|---|
 | 258 | +					return;  | 
|---|
 | 259 | +				}  | 
|---|
 | 260 | +				  | 
|---|
 | 261 | +			}  | 
|---|
 | 262 | +			Display.launchBrowser(e.getURL().toString());  | 
|---|
 | 263 | +			  | 
|---|
 | 264 | +		}   | 
|---|
 | 265 | +	}   | 
|---|
 | 266 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.views;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.app.ui.HtmlDisplay;  | 
|---|
 | 4 | +import timeflow.data.analysis.*;  | 
|---|
 | 5 | +import timeflow.data.db.*;  | 
|---|
 | 6 | +import timeflow.data.time.*;  | 
|---|
 | 7 | +import timeflow.format.field.FieldFormatCatalog;  | 
|---|
 | 8 | +import timeflow.model.*;  | 
|---|
 | 9 | +  | 
|---|
 | 10 | +  | 
|---|
 | 11 | +import java.awt.*;  | 
|---|
 | 12 | +import java.util.Date;  | 
|---|
 | 13 | +  | 
|---|
 | 14 | +import javax.swing.*;  | 
|---|
 | 15 | +  | 
|---|
 | 16 | +  | 
|---|
 | 17 | +public class SummaryView extends AbstractView {  | 
|---|
 | 18 | +	  | 
|---|
 | 19 | +	private JEditorPane analysisDisplay;  | 
|---|
 | 20 | +	private FieldAnalysis[] fieldAnalyzers=new FieldAnalysis[]  | 
|---|
 | 21 | +    {  | 
|---|
 | 22 | +			new MissingValueAnalysis(), new RangeDateAnalysis(),   | 
|---|
 | 23 | +			new RangeNumberAnalysis(), new FrequencyAnalysis()  | 
|---|
 | 24 | +    };  | 
|---|
 | 25 | +	private int numItems;  | 
|---|
 | 26 | +	private int numFiltered;  | 
|---|
 | 27 | +	private Interval range;  | 
|---|
 | 28 | +	private JComponent controls;  | 
|---|
 | 29 | +	  | 
|---|
 | 30 | +	public SummaryView(TFModel model)  | 
|---|
 | 31 | +	{  | 
|---|
 | 32 | +		super(model);  | 
|---|
 | 33 | +		analysisDisplay=HtmlDisplay.create();  | 
|---|
 | 34 | +		JScrollPane scrollPane = new JScrollPane(analysisDisplay);  | 
|---|
 | 35 | +		setLayout(new GridLayout(1,1));  | 
|---|
 | 36 | +		add(scrollPane);  | 
|---|
 | 37 | +		  | 
|---|
 | 38 | +		controls=new HtmlControls("This report gives a <br> statistical breakdown<br> "+  | 
|---|
 | 39 | +				"of your data. <p> Reading the summary often helps<br> you find "+  | 
|---|
 | 40 | +				"data errors.");  | 
|---|
 | 41 | +	}  | 
|---|
 | 42 | +	  | 
|---|
 | 43 | +	@Override  | 
|---|
 | 44 | +	public JComponent _getControls()  | 
|---|
 | 45 | +	{  | 
|---|
 | 46 | +		return controls;  | 
|---|
 | 47 | +	}  | 
|---|
 | 48 | +	  | 
|---|
 | 49 | +	void makeHtml()  | 
|---|
 | 50 | +	{  | 
|---|
 | 51 | +		Display d=getModel().getDisplay();  | 
|---|
 | 52 | +		ActDB db=getModel().getDB();  | 
|---|
 | 53 | +		ActList acts=getModel().getActs();  | 
|---|
 | 54 | +		StringBuffer page=new StringBuffer();  | 
|---|
 | 55 | +		page.append("<blockquote>");  | 
|---|
 | 56 | +		if (getModel().getDB()==null)  | 
|---|
 | 57 | +		{  | 
|---|
 | 58 | +			page.append("<h1><font color=#003399>No data loaded.</font></h1>");  | 
|---|
 | 59 | +		}  | 
|---|
 | 60 | +		else  | 
|---|
 | 61 | +		{  | 
|---|
 | 62 | +			page.append("<BR><BR><BR>File: "+getModel().getDbFile()+"<br>");  | 
|---|
 | 63 | +			page.append("Source: "+getModel().getDB().getSource()+"<br><br>");  | 
|---|
 | 64 | +			page.append("Description: "+getModel().getDB().getDescription()+"<br><br>");  | 
|---|
 | 65 | +			page.append("<br><br>");  | 
|---|
 | 66 | +			page.append("<table border=0>");  | 
|---|
 | 67 | +			  | 
|---|
 | 68 | +			  | 
|---|
 | 69 | +			page.append("<tr><td valign=top align=left width=100><b>");  | 
|---|
 | 70 | +			page.append("<font size=+1>Data</font><br>");  | 
|---|
 | 71 | +			  | 
|---|
 | 72 | +			page.append("</td><td align=top>");  | 
|---|
 | 73 | +			append(page, "Total events", ""+numItems);  | 
|---|
 | 74 | +			  | 
|---|
 | 75 | +			if (numItems>0)  | 
|---|
 | 76 | +			{  | 
|---|
 | 77 | +				append(page, "Total selected", ""+numFiltered);  | 
|---|
 | 78 | +				if (numFiltered>0)  | 
|---|
 | 79 | +				{  | 
|---|
 | 80 | +					append(page, "Earliest",  new Date(range.start).toString());  | 
|---|
 | 81 | +					append(page, "Latest",  new Date(range.end).toString());  | 
|---|
 | 82 | +				}  | 
|---|
 | 83 | +			}  | 
|---|
 | 84 | +			page.append("<br></td></tr>");  | 
|---|
 | 85 | +			  | 
|---|
 | 86 | +			page.append("<tr><td valign=top align=left width=100><b>");  | 
|---|
 | 87 | +			page.append("<font size=+1>Fields</font><br>");  | 
|---|
 | 88 | +			  | 
|---|
 | 89 | +			page.append("</td><td align=top>");  | 
|---|
 | 90 | +			for (Field f: getModel().getDB().getFields())  | 
|---|
 | 91 | +			{  | 
|---|
 | 92 | +					append(page, f.getName(),FieldFormatCatalog.humanName(f.getType())+fieldLabel(f));  | 
|---|
 | 93 | +			}  | 
|---|
 | 94 | +			page.append("<br></td></tr>");  | 
|---|
 | 95 | +			  | 
|---|
 | 96 | +				  | 
|---|
 | 97 | +			page.append("</table>");  | 
|---|
 | 98 | +  | 
|---|
 | 99 | +			if (numFiltered>0)  | 
|---|
 | 100 | +			{  | 
|---|
 | 101 | +				page.append("<h1>Statistics (for "+acts.size()+" items)</h1>");  | 
|---|
 | 102 | +				for (Field field: db.getFields())  | 
|---|
 | 103 | +				{  | 
|---|
 | 104 | +					page.append("<h2>"+field.getName()+"</h2>");  | 
|---|
 | 105 | +					page.append("<ul>");  | 
|---|
 | 106 | +					for (int i=0; i<fieldAnalyzers.length; i++)  | 
|---|
 | 107 | +					{  | 
|---|
 | 108 | +						FieldAnalysis fa=fieldAnalyzers[i];  | 
|---|
 | 109 | +						if (fa.canHandleType(field.getType()))  | 
|---|
 | 110 | +						{  | 
|---|
 | 111 | +							page.append("<li>");  | 
|---|
 | 112 | +							page.append("<b><font color=#808080>"+fa.getName()+"</font></b><br>");  | 
|---|
 | 113 | +							fa.perform(acts, field);  | 
|---|
 | 114 | +							String[] s=fa.getResultDescription();  | 
|---|
 | 115 | +							for (int j=0; j<s.length; j++)  | 
|---|
 | 116 | +							{  | 
|---|
 | 117 | +								page.append(s[j]);  | 
|---|
 | 118 | +								page.append("<br>");  | 
|---|
 | 119 | +							}  | 
|---|
 | 120 | +							page.append("</li>");  | 
|---|
 | 121 | +						}  | 
|---|
 | 122 | +					}  | 
|---|
 | 123 | +					page.append("</ul>");  | 
|---|
 | 124 | +				}  | 
|---|
 | 125 | +			}  | 
|---|
 | 126 | +		}  | 
|---|
 | 127 | +		page.append("</blockquote>");  | 
|---|
 | 128 | +		analysisDisplay.setText(page.toString());  | 
|---|
 | 129 | +		analysisDisplay.setCaretPosition(0);  | 
|---|
 | 130 | +	}  | 
|---|
 | 131 | +	  | 
|---|
 | 132 | +	static void append(StringBuffer page, String label, String value)  | 
|---|
 | 133 | +	{  | 
|---|
 | 134 | +		page.append("<b><font color=#808080>"+label+"</font></b>    ");  | 
|---|
 | 135 | +		page.append(value);  | 
|---|
 | 136 | +		page.append("<br>");  | 
|---|
 | 137 | +	}  | 
|---|
 | 138 | +	  | 
|---|
 | 139 | +	@Override  | 
|---|
 | 140 | +	protected void onscreen(boolean majorChange)  | 
|---|
 | 141 | +	{  | 
|---|
 | 142 | +		_note(null);  | 
|---|
 | 143 | +	}  | 
|---|
 | 144 | +  | 
|---|
 | 145 | +	protected void _note(TFEvent e) {  | 
|---|
 | 146 | +		recalculate();  | 
|---|
 | 147 | +		makeHtml();  | 
|---|
 | 148 | +		repaint();  | 
|---|
 | 149 | +	}  | 
|---|
 | 150 | +	  | 
|---|
 | 151 | +	String fieldLabel(Field f)  | 
|---|
 | 152 | +	{  | 
|---|
 | 153 | +		StringBuffer b=new StringBuffer("<b>");  | 
|---|
 | 154 | +		ActDB db=getModel().getDB();  | 
|---|
 | 155 | +		for (String v: VirtualField.list())  | 
|---|
 | 156 | +		{  | 
|---|
 | 157 | +			if (db.getField(v)!=null && db.getField(v).getName().equals(f.getName()))  | 
|---|
 | 158 | +				b.append(" (Shown in visualization as "+VirtualField.humanName(v)+")");  | 
|---|
 | 159 | +		}  | 
|---|
 | 160 | +		b.append("</b>");  | 
|---|
 | 161 | +		return b.toString();  | 
|---|
 | 162 | +	}  | 
|---|
 | 163 | +	  | 
|---|
 | 164 | +	void recalculate()  | 
|---|
 | 165 | +	{  | 
|---|
 | 166 | +		ActList acts=getModel().getActs();  | 
|---|
 | 167 | +		if (acts==null)  | 
|---|
 | 168 | +		{  | 
|---|
 | 169 | +			numItems=0;  | 
|---|
 | 170 | +			return;  | 
|---|
 | 171 | +		}  | 
|---|
 | 172 | +		numFiltered=acts.size();  | 
|---|
 | 173 | +		numItems=getModel().getDB().size();  | 
|---|
 | 174 | +		range=DBUtils.range(acts, VirtualField.START);  | 
|---|
 | 175 | +	}  | 
|---|
 | 176 | +  | 
|---|
 | 177 | +	@Override  | 
|---|
 | 178 | +	public String getName() {  | 
|---|
 | 179 | +		return "Summary";  | 
|---|
 | 180 | +	}  | 
|---|
 | 181 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.views;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.app.ui.HtmlDisplay;  | 
|---|
 | 4 | +import timeflow.data.db.*;  | 
|---|
 | 5 | +import timeflow.data.time.*;  | 
|---|
 | 6 | +import timeflow.format.field.DateTimeGuesser;  | 
|---|
 | 7 | +import timeflow.model.*;  | 
|---|
 | 8 | +  | 
|---|
 | 9 | +import java.awt.*;  | 
|---|
 | 10 | +import java.awt.event.ActionEvent;  | 
|---|
 | 11 | +import java.awt.event.ActionListener;  | 
|---|
 | 12 | +  | 
|---|
 | 13 | +import javax.swing.*;  | 
|---|
 | 14 | +import javax.swing.event.TableModelListener;  | 
|---|
 | 15 | +import javax.swing.table.*;  | 
|---|
 | 16 | +import java.util.*;  | 
|---|
 | 17 | +  | 
|---|
 | 18 | +public class TableView extends AbstractView {  | 
|---|
 | 19 | +  | 
|---|
 | 20 | +	private JTable table=new JTable();  | 
|---|
 | 21 | +	private int colorColumn=-1, labelColumn=-1;  | 
|---|
 | 22 | +	private Font font, bold;  | 
|---|
 | 23 | +	private boolean editable=true;  | 
|---|
 | 24 | +	private JPanel controls;  | 
|---|
 | 25 | +	  | 
|---|
 | 26 | +	public TableView(TFModel model)  | 
|---|
 | 27 | +	{  | 
|---|
 | 28 | +		super(model);  | 
|---|
 | 29 | +		  | 
|---|
 | 30 | +		JScrollPane scrollPane = new JScrollPane(table);  | 
|---|
 | 31 | +		table.setFillsViewportHeight(true);  | 
|---|
 | 32 | +		table.setAutoCreateRowSorter(true);  | 
|---|
 | 33 | +		font=model.getDisplay().plain();  | 
|---|
 | 34 | +		bold=model.getDisplay().bold();  | 
|---|
 | 35 | +		table.setFont(font);  | 
|---|
 | 36 | +		final int fh=getFontMetrics(font).getHeight();  | 
|---|
 | 37 | +		table.setRowHeight(fh+12);  | 
|---|
 | 38 | +		table.setShowGrid(false);  | 
|---|
 | 39 | +		table.getTableHeader().setPreferredSize(new Dimension(10, fh+15));  | 
|---|
 | 40 | +		table.getTableHeader().setFont(font);  | 
|---|
 | 41 | +		setReorderable(false);  | 
|---|
 | 42 | +		setLayout(new GridLayout(1,1));  | 
|---|
 | 43 | +		add(scrollPane);	  | 
|---|
 | 44 | +		  | 
|---|
 | 45 | +		controls=new HtmlControls("Use the table view for<br> a rapid overview<br> "+  | 
|---|
 | 46 | +				"of your data. <p>You can click<br> on the headers to sort the columns,<br> "+  | 
|---|
 | 47 | +				"and you can edit data<br> directly in the table cells.");  | 
|---|
 | 48 | +	}  | 
|---|
 | 49 | +	  | 
|---|
 | 50 | +	@Override  | 
|---|
 | 51 | +	public JComponent _getControls()  | 
|---|
 | 52 | +	{  | 
|---|
 | 53 | +		return controls;  | 
|---|
 | 54 | +	}  | 
|---|
 | 55 | +	  | 
|---|
 | 56 | +	public void setEditable(boolean editable)  | 
|---|
 | 57 | +	{  | 
|---|
 | 58 | +		this.editable=editable;  | 
|---|
 | 59 | +	}  | 
|---|
 | 60 | +	  | 
|---|
 | 61 | +	public JTable getTable()  | 
|---|
 | 62 | +	{  | 
|---|
 | 63 | +		return table;  | 
|---|
 | 64 | +	}  | 
|---|
 | 65 | +	  | 
|---|
 | 66 | +	@Override  | 
|---|
 | 67 | +	public void onscreen(boolean majorChange)  | 
|---|
 | 68 | +	{  | 
|---|
 | 69 | +		_note(null);  | 
|---|
 | 70 | +	}  | 
|---|
 | 71 | +	  | 
|---|
 | 72 | +	@Override  | 
|---|
 | 73 | +	protected void _note(TFEvent e) {  | 
|---|
 | 74 | +		setActs(getModel().getActs());		  | 
|---|
 | 75 | +	}  | 
|---|
 | 76 | +	  | 
|---|
 | 77 | +	public void setActs(ActList acts)  | 
|---|
 | 78 | +	{  | 
|---|
 | 79 | +		TableModel t=new TimelineTableModel(acts);  | 
|---|
 | 80 | +		table.setModel(t);  | 
|---|
 | 81 | +		ActTableRenderer r=new ActTableRenderer(acts);  | 
|---|
 | 82 | +		table.setDefaultRenderer(Object.class, r);  | 
|---|
 | 83 | +		table.setDefaultRenderer(Double.class, r);  | 
|---|
 | 84 | +		table.setDefaultEditor(String[].class, new StringArrayEditor());  | 
|---|
 | 85 | +		table.setDefaultEditor(RoughTime.class, new RoughTimeEditor());  | 
|---|
 | 86 | +		repaint();  | 
|---|
 | 87 | +	}  | 
|---|
 | 88 | +	  | 
|---|
 | 89 | +	@Override  | 
|---|
 | 90 | +	public String getName() {  | 
|---|
 | 91 | +		return "Table";  | 
|---|
 | 92 | +	}  | 
|---|
 | 93 | +  | 
|---|
 | 94 | +	  | 
|---|
 | 95 | +	class ActTableRenderer extends DefaultTableCellRenderer {  | 
|---|
 | 96 | +		  | 
|---|
 | 97 | +		ActList acts;  | 
|---|
 | 98 | +		Color zebra=new Color(240,240,240);  | 
|---|
 | 99 | +		boolean color=false;  | 
|---|
 | 100 | +		Color dataColor;  | 
|---|
 | 101 | +		  | 
|---|
 | 102 | +		ActTableRenderer(ActList acts)  | 
|---|
 | 103 | +		{  | 
|---|
 | 104 | +			this.acts=acts;  | 
|---|
 | 105 | +		}  | 
|---|
 | 106 | +		  | 
|---|
 | 107 | +	    public void setValue(Object value) {  | 
|---|
 | 108 | +	    	if (value==null)  | 
|---|
 | 109 | +	    	{  | 
|---|
 | 110 | +	    		super.setValue("");  | 
|---|
 | 111 | +	    		return;  | 
|---|
 | 112 | +	    	}  | 
|---|
 | 113 | +	    	setHorizontalAlignment(value instanceof Double ? SwingConstants.RIGHT : SwingConstants.LEFT);  | 
|---|
 | 114 | +	    	super.setValue(getModel().getDisplay().toString(value));  | 
|---|
 | 115 | +	    }  | 
|---|
 | 116 | +  | 
|---|
 | 117 | +	    public Component getTableCellRendererComponent(JTable table, Object value,  | 
|---|
 | 118 | +	            boolean isSelected, boolean hasFocus, int rowIndex, int vColIndex) {  | 
|---|
 | 119 | +  | 
|---|
 | 120 | +	    	if (vColIndex==labelColumn || vColIndex==colorColumn)  | 
|---|
 | 121 | +	    		setFont(bold);  | 
|---|
 | 122 | +	    	else  | 
|---|
 | 123 | +	    		setFont(font);  | 
|---|
 | 124 | +	    	setBackground(rowIndex%2==0 ? Color.white : zebra);  | 
|---|
 | 125 | +	    	color=vColIndex==colorColumn || vColIndex==labelColumn;  | 
|---|
 | 126 | +	    	Field colorField=null;  | 
|---|
 | 127 | +	    	if (color)  | 
|---|
 | 128 | +	    	{  | 
|---|
 | 129 | +	    		colorField=getModel().getColorField();  | 
|---|
 | 130 | +	    		color &= colorField!=null;  | 
|---|
 | 131 | +	    	}  | 
|---|
 | 132 | +	    	if (color)  | 
|---|
 | 133 | +	    	{  | 
|---|
 | 134 | +		    	int actIndex=table.convertRowIndexToModel(rowIndex);  | 
|---|
 | 135 | +		    	Act act=acts.get(actIndex);  | 
|---|
 | 136 | +		    	  | 
|---|
 | 137 | +		    	if (colorField==null || colorField.getType()!=String.class)  | 
|---|
 | 138 | +		    		dataColor=getModel().getDisplay().getColor("timeline.unspecified.color");  | 
|---|
 | 139 | +		    	else  | 
|---|
 | 140 | +		    		dataColor=getModel().getDisplay().makeColor(act.getString(colorField));  | 
|---|
 | 141 | +		    	setForeground(dataColor);  | 
|---|
 | 142 | +		    	setValue(value);  | 
|---|
 | 143 | +	    	}  | 
|---|
 | 144 | +	    	else  | 
|---|
 | 145 | +	    	{  | 
|---|
 | 146 | +	    		setForeground(Color.black);  | 
|---|
 | 147 | +	    		setValue(value);  | 
|---|
 | 148 | +	    	}  | 
|---|
 | 149 | +	        return this;  | 
|---|
 | 150 | +	    }	      | 
|---|
 | 151 | +	}  | 
|---|
 | 152 | +	  | 
|---|
 | 153 | +	class TimelineTableModel implements TableModel  | 
|---|
 | 154 | +	{  | 
|---|
 | 155 | +		ActList acts;  | 
|---|
 | 156 | +		Field[] fields;  | 
|---|
 | 157 | +		  | 
|---|
 | 158 | +		TimelineTableModel(ActList acts)  | 
|---|
 | 159 | +		{  | 
|---|
 | 160 | +			this.acts=acts;  | 
|---|
 | 161 | +			ArrayList<Field> a=new ArrayList<Field>();  | 
|---|
 | 162 | +			int i=0;  | 
|---|
 | 163 | +			Field colorField=getModel().getColorField();  | 
|---|
 | 164 | +			Field labelField=getModel().getDB().getField(VirtualField.LABEL);  | 
|---|
 | 165 | +			for (Field f:acts.getDB().getFields())  | 
|---|
 | 166 | +			{  | 
|---|
 | 167 | +				a.add(f);  | 
|---|
 | 168 | +				if (f==colorField)  | 
|---|
 | 169 | +					colorColumn=i;  | 
|---|
 | 170 | +				if (f==labelField)  | 
|---|
 | 171 | +					labelColumn=i;  | 
|---|
 | 172 | +				i++;  | 
|---|
 | 173 | +			}  | 
|---|
 | 174 | +			fields=(Field[])a.toArray(new Field[0]);  | 
|---|
 | 175 | +		}  | 
|---|
 | 176 | +		  | 
|---|
 | 177 | +		@Override  | 
|---|
 | 178 | +		public void addTableModelListener(TableModelListener l) {  | 
|---|
 | 179 | +		}  | 
|---|
 | 180 | +  | 
|---|
 | 181 | +		@Override  | 
|---|
 | 182 | +		public Class<?> getColumnClass(int columnIndex) {  | 
|---|
 | 183 | +			return fields[columnIndex].getType();  | 
|---|
 | 184 | +		}  | 
|---|
 | 185 | +  | 
|---|
 | 186 | +		@Override  | 
|---|
 | 187 | +		public int getColumnCount() {  | 
|---|
 | 188 | +			return fields.length;  | 
|---|
 | 189 | +		}  | 
|---|
 | 190 | +  | 
|---|
 | 191 | +		@Override  | 
|---|
 | 192 | +		public String getColumnName(int columnIndex) {  | 
|---|
 | 193 | +			return fields[columnIndex].getName();  | 
|---|
 | 194 | +		}  | 
|---|
 | 195 | +  | 
|---|
 | 196 | +		@Override  | 
|---|
 | 197 | +		public int getRowCount() {  | 
|---|
 | 198 | +			return acts.size();  | 
|---|
 | 199 | +		}  | 
|---|
 | 200 | +  | 
|---|
 | 201 | +		@Override  | 
|---|
 | 202 | +		public Object getValueAt(int rowIndex, int columnIndex) {  | 
|---|
 | 203 | +			return acts.get(rowIndex).get(fields[columnIndex]);  | 
|---|
 | 204 | +		}  | 
|---|
 | 205 | +  | 
|---|
 | 206 | +		@Override  | 
|---|
 | 207 | +		public boolean isCellEditable(int rowIndex, int columnIndex) {  | 
|---|
 | 208 | +			return editable;  | 
|---|
 | 209 | +		}  | 
|---|
 | 210 | +  | 
|---|
 | 211 | +		@Override  | 
|---|
 | 212 | +		public void removeTableModelListener(TableModelListener l) {  | 
|---|
 | 213 | +		}  | 
|---|
 | 214 | +  | 
|---|
 | 215 | +		@Override  | 
|---|
 | 216 | +		public void setValueAt(Object aValue, int rowIndex, int columnIndex) {  | 
|---|
 | 217 | +			acts.get(rowIndex).set(fields[columnIndex], aValue);  | 
|---|
 | 218 | +			getModel().noteRecordChange(TableView.this);  | 
|---|
 | 219 | +		}  | 
|---|
 | 220 | +	}  | 
|---|
 | 221 | +	  | 
|---|
 | 222 | +	public class StringArrayEditor extends AbstractCellEditor implements TableCellEditor   | 
|---|
 | 223 | +	{  | 
|---|
 | 224 | +	    JComponent component = new JTextField();  | 
|---|
 | 225 | +  | 
|---|
 | 226 | +	    public Component getTableCellEditorComponent(JTable table, Object value,  | 
|---|
 | 227 | +	            boolean isSelected, int rowIndex, int vColIndex) {  | 
|---|
 | 228 | +	        ((JTextField)component).setText(getModel().getDisplay().toString(value));  | 
|---|
 | 229 | +	        return component;  | 
|---|
 | 230 | +	    }  | 
|---|
 | 231 | +  | 
|---|
 | 232 | +	    public Object getCellEditorValue()   | 
|---|
 | 233 | +	    {  | 
|---|
 | 234 | +	        String s= ((JTextField)component).getText();  | 
|---|
 | 235 | +	        String[] tags=s.split(",");  | 
|---|
 | 236 | +	        for (int i=0; i<tags.length; i++)  | 
|---|
 | 237 | +	        	tags[i]=tags[i].trim();  | 
|---|
 | 238 | +	        return tags;  | 
|---|
 | 239 | +	    }  | 
|---|
 | 240 | +	}  | 
|---|
 | 241 | +	  | 
|---|
 | 242 | +	public class RoughTimeEditor extends AbstractCellEditor implements TableCellEditor   | 
|---|
 | 243 | +	{  | 
|---|
 | 244 | +	    JComponent component = new JTextField();  | 
|---|
 | 245 | +	    DateTimeGuesser guesser=new DateTimeGuesser();  | 
|---|
 | 246 | +  | 
|---|
 | 247 | +	    public Component getTableCellEditorComponent(JTable table, Object value,  | 
|---|
 | 248 | +	            boolean isSelected, int rowIndex, int vColIndex) {  | 
|---|
 | 249 | +	    	  | 
|---|
 | 250 | +	    	RoughTime r=(RoughTime)value;  | 
|---|
 | 251 | +	    	JTextField t=((JTextField)component);  | 
|---|
 | 252 | +	        t.setText(r==null ? "" : r.format());  | 
|---|
 | 253 | +	        return component;  | 
|---|
 | 254 | +	    }  | 
|---|
 | 255 | +  | 
|---|
 | 256 | +	    public Object getCellEditorValue()   | 
|---|
 | 257 | +	    {  | 
|---|
 | 258 | +	        String s= ((JTextField)component).getText();  | 
|---|
 | 259 | +	        return s.trim().length()==0 ? null : guesser.guess(s);  | 
|---|
 | 260 | +	    }  | 
|---|
 | 261 | +	}  | 
|---|
 | 262 | +  | 
|---|
 | 263 | +	public void setReorderable(boolean allow) {  | 
|---|
 | 264 | +		table.getTableHeader().setReorderingAllowed(allow);  | 
|---|
 | 265 | +	}  | 
|---|
 | 266 | +  | 
|---|
 | 267 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.views;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.app.ui.ComponentCluster;  | 
|---|
 | 4 | +import timeflow.data.db.*;  | 
|---|
 | 5 | +import timeflow.data.time.*;  | 
|---|
 | 6 | +import timeflow.model.*;  | 
|---|
 | 7 | +import timeflow.views.CalendarView.CalendarPanel;  | 
|---|
 | 8 | +import timeflow.views.CalendarView.ScrollingCalendar;  | 
|---|
 | 9 | +import timeflow.vis.*;  | 
|---|
 | 10 | +import timeflow.vis.timeline.*;  | 
|---|
 | 11 | +  | 
|---|
 | 12 | +import java.awt.*;  | 
|---|
 | 13 | +import java.awt.event.*;  | 
|---|
 | 14 | +import java.awt.image.*;  | 
|---|
 | 15 | +  | 
|---|
 | 16 | +import javax.swing.*;  | 
|---|
 | 17 | +  | 
|---|
 | 18 | +import java.util.*;  | 
|---|
 | 19 | +  | 
|---|
 | 20 | +public class TimelineView extends AbstractView  | 
|---|
 | 21 | +{  | 
|---|
 | 22 | +        AxisRenderer grid;  | 
|---|
 | 23 | +        TimelineRenderer timeline;  | 
|---|
 | 24 | +        TimelineVisuals visuals;  | 
|---|
 | 25 | +        TimelinePanel timelinePanel;  | 
|---|
 | 26 | +        JButton fit;  | 
|---|
 | 27 | +        ScrollingTimeline scroller;  | 
|---|
 | 28 | +        JPanel controls;  | 
|---|
 | 29 | +        JScrollBar bar;  | 
|---|
 | 30 | +  | 
|---|
 | 31 | +        //JScrollPane scrollpane;  | 
|---|
 | 32 | +        public JComponent _getControls()  | 
|---|
 | 33 | +        {  | 
|---|
 | 34 | +                return controls;  | 
|---|
 | 35 | +        }  | 
|---|
 | 36 | +  | 
|---|
 | 37 | +        public TimelineView(TFModel model)  | 
|---|
 | 38 | +        {  | 
|---|
 | 39 | +                super(model);  | 
|---|
 | 40 | +                visuals = new TimelineVisuals(model);  | 
|---|
 | 41 | +                grid = new AxisRenderer(visuals);  | 
|---|
 | 42 | +                timeline = new TimelineRenderer(visuals);  | 
|---|
 | 43 | +  | 
|---|
 | 44 | +                timelinePanel = new TimelinePanel(model);  | 
|---|
 | 45 | +                scroller = new ScrollingTimeline();  | 
|---|
 | 46 | +                setLayout(new BorderLayout());  | 
|---|
 | 47 | +                add(scroller, BorderLayout.CENTER);  | 
|---|
 | 48 | +  | 
|---|
 | 49 | +                JPanel bottom = new JPanel();  | 
|---|
 | 50 | +                bottom.setLayout(new BorderLayout());  | 
|---|
 | 51 | +                add(bottom, BorderLayout.SOUTH);  | 
|---|
 | 52 | +  | 
|---|
 | 53 | +                TimelineSlider slider = new TimelineSlider(visuals, 24 * 60 * 60 * 1000L, new Runnable()  | 
|---|
 | 54 | +                {  | 
|---|
 | 55 | +  | 
|---|
 | 56 | +                        @Override  | 
|---|
 | 57 | +                        public void run()  | 
|---|
 | 58 | +                        {  | 
|---|
 | 59 | +                                redraw();  | 
|---|
 | 60 | +                        }  | 
|---|
 | 61 | +                });  | 
|---|
 | 62 | +                bottom.add(slider, BorderLayout.CENTER);  | 
|---|
 | 63 | +  | 
|---|
 | 64 | +                controls = new JPanel();  | 
|---|
 | 65 | +                controls.setBackground(Color.white);  | 
|---|
 | 66 | +                controls.setLayout(new BorderLayout());//new GridLayout(2,1));  | 
|---|
 | 67 | +  | 
|---|
 | 68 | +                // top part of grid: zoom buttons.  | 
|---|
 | 69 | +                ComponentCluster buttons = new ComponentCluster("Zoom");  | 
|---|
 | 70 | +                ImageIcon zoomOutIcon = new ImageIcon("images/zoom_out.gif");  | 
|---|
 | 71 | +                JButton zoomOut = new JButton(zoomOutIcon);  | 
|---|
 | 72 | +                buttons.addContent(zoomOut);  | 
|---|
 | 73 | +                zoomOut.addActionListener(new ActionListener()  | 
|---|
 | 74 | +                {  | 
|---|
 | 75 | +  | 
|---|
 | 76 | +                        @Override  | 
|---|
 | 77 | +                        public void actionPerformed(ActionEvent e)  | 
|---|
 | 78 | +                        {  | 
|---|
 | 79 | +                                Interval zoom = visuals.getViewInterval().subinterval(-1, 2).intersection(visuals.getGlobalInterval());  | 
|---|
 | 80 | +                                moveTime(zoom);  | 
|---|
 | 81 | +                        }  | 
|---|
 | 82 | +                });  | 
|---|
 | 83 | +  | 
|---|
 | 84 | +                ImageIcon zoomOut100Icon = new ImageIcon("images/zoom_out_100.gif");  | 
|---|
 | 85 | +                JButton zoomOutAll = new JButton(zoomOut100Icon);  | 
|---|
 | 86 | +                buttons.addContent(zoomOutAll);  | 
|---|
 | 87 | +                zoomOutAll.addActionListener(new ActionListener()  | 
|---|
 | 88 | +                {  | 
|---|
 | 89 | +  | 
|---|
 | 90 | +                        @Override  | 
|---|
 | 91 | +                        public void actionPerformed(ActionEvent e)  | 
|---|
 | 92 | +                        {  | 
|---|
 | 93 | +                                moveTime(visuals.getGlobalInterval());  | 
|---|
 | 94 | +                        }  | 
|---|
 | 95 | +                });  | 
|---|
 | 96 | +  | 
|---|
 | 97 | +                /*  | 
|---|
 | 98 | +                // UI for zooming to precisely fit the visible selection.  | 
|---|
 | 99 | +                // No one seemed to think this was so important, but we may want it again some day.  | 
|---|
 | 100 | +                // if you uncomment this, then also uncomment the line in reset().  | 
|---|
 | 101 | +                ImageIcon zoomSelection=new ImageIcon("images/zoom_selection.gif");  | 
|---|
 | 102 | +                fit=new JButton(zoomSelection);  | 
|---|
 | 103 | +                fit.setBackground(Color.white);  | 
|---|
 | 104 | +                buttons.addContent(fit);  | 
|---|
 | 105 | +                fit.setEnabled(false);  | 
|---|
 | 106 | +                fit.addActionListener(new ActionListener() {  | 
|---|
 | 107 | +                @Override  | 
|---|
 | 108 | +                public void actionPerformed(ActionEvent e) {  | 
|---|
 | 109 | +                moveTime(visuals.getFitToVisibleRange());  | 
|---|
 | 110 | +                }});  | 
|---|
 | 111 | +                 */  | 
|---|
 | 112 | +                controls.add(buttons, BorderLayout.NORTH);  | 
|---|
 | 113 | +  | 
|---|
 | 114 | +                // ok, now do second part of grid: layout style buttons.  | 
|---|
 | 115 | +                ComponentCluster layoutPanel = new ComponentCluster("Layout");  | 
|---|
 | 116 | +  | 
|---|
 | 117 | +                ButtonGroup layoutGroup = new ButtonGroup();  | 
|---|
 | 118 | +  | 
|---|
 | 119 | +                ImageIcon diagonalIcon = new ImageIcon("images/layout_diagonal.gif");  | 
|---|
 | 120 | +                JRadioButton diagonal = new JRadioButton(diagonalIcon, true);  | 
|---|
 | 121 | +                diagonal.setSelectedIcon(new ImageIcon("images/layout_diagonal_selected.gif"));  | 
|---|
 | 122 | +                layoutPanel.addContent(diagonal);  | 
|---|
 | 123 | +                diagonal.addActionListener(new LayoutSetter(TimelineVisuals.Layout.TIGHT));  | 
|---|
 | 124 | +                layoutGroup.add(diagonal);  | 
|---|
 | 125 | +  | 
|---|
 | 126 | +                ImageIcon looseIcon = new ImageIcon("images/layout_loose.gif");  | 
|---|
 | 127 | +                JRadioButton loose = new JRadioButton(looseIcon, false);  | 
|---|
 | 128 | +                loose.setSelectedIcon(new ImageIcon("images/layout_loose_selected.gif"));  | 
|---|
 | 129 | +                layoutPanel.addContent(loose);  | 
|---|
 | 130 | +                loose.addActionListener(new LayoutSetter(TimelineVisuals.Layout.LOOSE));  | 
|---|
 | 131 | +                layoutGroup.add(loose);  | 
|---|
 | 132 | +  | 
|---|
 | 133 | +                ImageIcon graphIcon = new ImageIcon("images/layout_graph.gif");  | 
|---|
 | 134 | +                JRadioButton graph = new JRadioButton(graphIcon, false);  | 
|---|
 | 135 | +                graph.setSelectedIcon(new ImageIcon("images/layout_graph_selected.gif"));  | 
|---|
 | 136 | +                layoutPanel.addContent(graph);  | 
|---|
 | 137 | +                graph.addActionListener(new LayoutSetter(TimelineVisuals.Layout.GRAPH));  | 
|---|
 | 138 | +                layoutGroup.add(graph);  | 
|---|
 | 139 | +  | 
|---|
 | 140 | +                controls.add(layoutPanel, BorderLayout.CENTER);  | 
|---|
 | 141 | +        }  | 
|---|
 | 142 | +  | 
|---|
 | 143 | +        class LayoutSetter implements ActionListener  | 
|---|
 | 144 | +        {  | 
|---|
 | 145 | +                TimelineVisuals.Layout layout;  | 
|---|
 | 146 | +  | 
|---|
 | 147 | +                LayoutSetter(TimelineVisuals.Layout layout)  | 
|---|
 | 148 | +                {  | 
|---|
 | 149 | +                        this.layout = layout;  | 
|---|
 | 150 | +                }  | 
|---|
 | 151 | +  | 
|---|
 | 152 | +                @Override  | 
|---|
 | 153 | +                public void actionPerformed(ActionEvent e)  | 
|---|
 | 154 | +                {  | 
|---|
 | 155 | +                        visuals.setLayoutStyle(layout);  | 
|---|
 | 156 | +                        redraw();  | 
|---|
 | 157 | +                }  | 
|---|
 | 158 | +        }  | 
|---|
 | 159 | +  | 
|---|
 | 160 | +        void moveTime(Interval interval)  | 
|---|
 | 161 | +        {  | 
|---|
 | 162 | +                new TimeAnimator(interval).start();  | 
|---|
 | 163 | +        }  | 
|---|
 | 164 | +  | 
|---|
 | 165 | +        void redraw()  | 
|---|
 | 166 | +        {  | 
|---|
 | 167 | +                visuals.layout();  | 
|---|
 | 168 | +                timelinePanel.drawVisualization();  | 
|---|
 | 169 | +                repaint();  | 
|---|
 | 170 | +        }  | 
|---|
 | 171 | +  | 
|---|
 | 172 | +        @Override  | 
|---|
 | 173 | +        protected void onscreen(boolean majorChange)  | 
|---|
 | 174 | +        {  | 
|---|
 | 175 | +                visuals.init(majorChange);  | 
|---|
 | 176 | +                reset(majorChange);  | 
|---|
 | 177 | +                redraw();  | 
|---|
 | 178 | +                scroller.calibrate();  | 
|---|
 | 179 | +        }  | 
|---|
 | 180 | +  | 
|---|
 | 181 | +        @Override  | 
|---|
 | 182 | +        protected void _note(TFEvent e)  | 
|---|
 | 183 | +        {  | 
|---|
 | 184 | +                visuals.note(e);  | 
|---|
 | 185 | +                reset(e.affectsRowSet());  | 
|---|
 | 186 | +        }  | 
|---|
 | 187 | +  | 
|---|
 | 188 | +        void reset(boolean forceViewChange)  | 
|---|
 | 189 | +        {  | 
|---|
 | 190 | +                if (forceViewChange || getModel().getViewInterval() == null)  | 
|---|
 | 191 | +                {  | 
|---|
 | 192 | +                        int numSelected = getModel().getActs().size();  | 
|---|
 | 193 | +                        int numVisible = DBUtils.count(getModel().getActs(), visuals.getViewInterval(),  | 
|---|
 | 194 | +                                getModel().getDB().getField(VirtualField.START));  | 
|---|
 | 195 | +                        if (numVisible < 10 && numSelected > numVisible)  | 
|---|
 | 196 | +                        {  | 
|---|
 | 197 | +                                moveTime(visuals.getFitToVisibleRange());  | 
|---|
 | 198 | +                        }  | 
|---|
 | 199 | +                }  | 
|---|
 | 200 | +                // uncomment this if we are using the fit button again.  | 
|---|
 | 201 | +                //fit.setEnabled(getModel().getActs().size()<getModel().getDB().size());		  | 
|---|
 | 202 | +                redraw();  | 
|---|
 | 203 | +                scroller.calibrate();  | 
|---|
 | 204 | +        }  | 
|---|
 | 205 | +  | 
|---|
 | 206 | +        class TimeAnimator extends Thread  | 
|---|
 | 207 | +        {  | 
|---|
 | 208 | +                Interval i1, i2;  | 
|---|
 | 209 | +  | 
|---|
 | 210 | +                TimeAnimator(Interval i2)  | 
|---|
 | 211 | +                {  | 
|---|
 | 212 | +                        this.i1 = visuals.getViewInterval();  | 
|---|
 | 213 | +                        this.i2 = i2;  | 
|---|
 | 214 | +                }  | 
|---|
 | 215 | +  | 
|---|
 | 216 | +                TimeAnimator(Interval i1, Interval i2)  | 
|---|
 | 217 | +                {  | 
|---|
 | 218 | +                        this.i1 = i1;  | 
|---|
 | 219 | +                        this.i2 = i2;  | 
|---|
 | 220 | +                }  | 
|---|
 | 221 | +  | 
|---|
 | 222 | +                public void run()  | 
|---|
 | 223 | +                {  | 
|---|
 | 224 | +                        int n = 15;  | 
|---|
 | 225 | +                        for (int i = 0; i < n; i++)  | 
|---|
 | 226 | +                        {  | 
|---|
 | 227 | +                                long start = ((n - i) * i1.start + i * i2.start) / n;  | 
|---|
 | 228 | +                                long end = ((n - i) * i1.end + i * i2.end) / n;  | 
|---|
 | 229 | +                                try  | 
|---|
 | 230 | +                                {  | 
|---|
 | 231 | +                                        visuals.setTimeBounds(start, end);  | 
|---|
 | 232 | +                                        redraw();  | 
|---|
 | 233 | +                                          | 
|---|
 | 234 | +                                        sleep(20);  | 
|---|
 | 235 | +                                } catch (Exception e)  | 
|---|
 | 236 | +                                {  | 
|---|
 | 237 | +                                }  | 
|---|
 | 238 | +                        }  | 
|---|
 | 239 | +                }  | 
|---|
 | 240 | +        }  | 
|---|
 | 241 | +  | 
|---|
 | 242 | +        class ScrollingTimeline extends JPanel  | 
|---|
 | 243 | +        {  | 
|---|
 | 244 | +                //JScrollBar bar;  | 
|---|
 | 245 | +  | 
|---|
 | 246 | +                public ScrollingTimeline()  | 
|---|
 | 247 | +                {  | 
|---|
 | 248 | +                        setLayout(new BorderLayout());  | 
|---|
 | 249 | +                        //scrollpane = new JScrollPane(timelinePanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);  | 
|---|
 | 250 | +                        //add(scrollpane, BorderLayout.CENTER);  | 
|---|
 | 251 | +                        add(timelinePanel, BorderLayout.CENTER);  | 
|---|
 | 252 | +                        bar = new JScrollBar(JScrollBar.VERTICAL);  | 
|---|
 | 253 | +                        add(bar, BorderLayout.EAST);  | 
|---|
 | 254 | +                        //bar = scrollpane.getVerticalScrollBar();  | 
|---|
 | 255 | +                        bar.addAdjustmentListener(new AdjustmentListener()  | 
|---|
 | 256 | +                        {  | 
|---|
 | 257 | +  | 
|---|
 | 258 | +                                @Override  | 
|---|
 | 259 | +                                public void adjustmentValueChanged(AdjustmentEvent e)  | 
|---|
 | 260 | +                                {  | 
|---|
 | 261 | +                                        timeline.setDY(bar.getValue());  | 
|---|
 | 262 | +                                        timelinePanel.drawVisualization();  | 
|---|
 | 263 | +                                        //timelinePanel.repaint();  | 
|---|
 | 264 | +                                }  | 
|---|
 | 265 | +                        });  | 
|---|
 | 266 | +                }  | 
|---|
 | 267 | +  | 
|---|
 | 268 | +                public void setBounds(int x, int y, int w, int h)  | 
|---|
 | 269 | +                {  | 
|---|
 | 270 | +                        super.setBounds(x, y, w, h);  | 
|---|
 | 271 | +                        calibrate();  | 
|---|
 | 272 | +                }  | 
|---|
 | 273 | +  | 
|---|
 | 274 | +                void calibrate()  | 
|---|
 | 275 | +                {  | 
|---|
 | 276 | +//                    timelinePanel.setBorder(BorderFactory.createLineBorder(Color.red, 2));  | 
|---|
 | 277 | +                        //javax.swing.border.Border b = scrollpane.getViewportBorder();  | 
|---|
 | 278 | +                        //scrollpane.setViewportBorder(BorderFactory.createLineBorder(Color.red, 2));  | 
|---|
 | 279 | +//                    scrollpane.getViewport().setBounds(0,0,100,100); //.setViewSize(new Dimension(100, visuals.getFullHeight()));  | 
|---|
 | 280 | +//			if (true) // visuals==null)  | 
|---|
 | 281 | +//				return;  | 
|---|
 | 282 | +                        final int height = getSize().height * 90 / 100; // room for bottom  | 
|---|
 | 283 | +                        final int desired = Math.max(height, visuals.getFullHeight());  | 
|---|
 | 284 | +//                        bar.setVisible(desired > height);  | 
|---|
 | 285 | +//                        bar.setMinimum(0);   // is this double setting necessary?  | 
|---|
 | 286 | +//                        bar.setMaximum(desired); // more testing is needed, it solved problems  | 
|---|
 | 287 | +//                        bar.setVisibleAmount(height);  // on certain Macs are one point.  | 
|---|
 | 288 | +                        SwingUtilities.invokeLater(new Runnable()  | 
|---|
 | 289 | +                        {  | 
|---|
 | 290 | +  | 
|---|
 | 291 | +                                @Override  | 
|---|
 | 292 | +                                public void run()  | 
|---|
 | 293 | +                                {  | 
|---|
 | 294 | +                                        bar.setMinimum(0);  | 
|---|
 | 295 | +                                        bar.setMaximum(desired);  | 
|---|
 | 296 | +                                        bar.setVisibleAmount(height);  | 
|---|
 | 297 | +                                }  | 
|---|
 | 298 | +                        });  | 
|---|
 | 299 | +                }  | 
|---|
 | 300 | +        }  | 
|---|
 | 301 | +  | 
|---|
 | 302 | +        class TimelinePanel extends AbstractVisualizationView  | 
|---|
 | 303 | +        {  | 
|---|
 | 304 | +                public TimelinePanel(TFModel model)  | 
|---|
 | 305 | +                {  | 
|---|
 | 306 | +                        super(model);  | 
|---|
 | 307 | +                          | 
|---|
 | 308 | +                        addMouseListener(new MouseAdapter()  | 
|---|
 | 309 | +                        {  | 
|---|
 | 310 | +                                @Override  | 
|---|
 | 311 | +                                public void mouseClicked(MouseEvent e)  | 
|---|
 | 312 | +                                {  | 
|---|
 | 313 | +                                        if (e.getClickCount() == 2)  | 
|---|
 | 314 | +                                        {  | 
|---|
 | 315 | +                                                moveTime(visuals.getViewInterval().subinterval(.333, .667));  | 
|---|
 | 316 | +                                        }  | 
|---|
 | 317 | +                                }  | 
|---|
 | 318 | +  | 
|---|
 | 319 | +                                @Override  | 
|---|
 | 320 | +                                public void mouseExited(MouseEvent e)  | 
|---|
 | 321 | +                                {  | 
|---|
 | 322 | +                                        mouse.setLocation(new Point(-1, -1));  | 
|---|
 | 323 | +                                        repaint();  | 
|---|
 | 324 | +                                }  | 
|---|
 | 325 | +  | 
|---|
 | 326 | +                                @Override  | 
|---|
 | 327 | +                                public void mousePressed(MouseEvent e)  | 
|---|
 | 328 | +                                {  | 
|---|
 | 329 | +                                        // was this a right-click or ctrl-click? ignore.  | 
|---|
 | 330 | +                                        if (e.isPopupTrigger())  | 
|---|
 | 331 | +                                        {  | 
|---|
 | 332 | +                                                return;  | 
|---|
 | 333 | +                                        }  | 
|---|
 | 334 | +                                        // did we click on a date label?  | 
|---|
 | 335 | +                                        for (Mouseover o : objectLocations)  | 
|---|
 | 336 | +                                        {  | 
|---|
 | 337 | +                                                if (o.contains(e.getX(), e.getY()) && o.thing instanceof Interval)  | 
|---|
 | 338 | +                                                {  | 
|---|
 | 339 | +                                                        moveTime((Interval) o.thing);  | 
|---|
 | 340 | +                                                        return;  | 
|---|
 | 341 | +                                                }  | 
|---|
 | 342 | +                                        }  | 
|---|
 | 343 | +                                        // if not, prepare  | 
|---|
 | 344 | +                                        firstMouse.setLocation(e.getX(), e.getY());  | 
|---|
 | 345 | +                                        mouseIsDown = true;  | 
|---|
 | 346 | +                                        repaint();  | 
|---|
 | 347 | +                                }  | 
|---|
 | 348 | +  | 
|---|
 | 349 | +                                @Override  | 
|---|
 | 350 | +                                public void mouseReleased(MouseEvent event)  | 
|---|
 | 351 | +                                {  | 
|---|
 | 352 | +                                        if (!mouseIsDown) // this means we had clicked on a date label.  | 
|---|
 | 353 | +                                        {  | 
|---|
 | 354 | +                                                return;  | 
|---|
 | 355 | +                                        }  | 
|---|
 | 356 | +                                          | 
|---|
 | 357 | +                                        mouseIsDown = false;  | 
|---|
 | 358 | +                                        int a = firstMouse.x;  | 
|---|
 | 359 | +                                        int b = mouse.x;  | 
|---|
 | 360 | +                                          | 
|---|
 | 361 | +                                        long start = visuals.getTimeScale().toTime(a);  | 
|---|
 | 362 | +                                        long end = visuals.getTimeScale().toTime(b);  | 
|---|
 | 363 | +                                        if (b - a < 0)  | 
|---|
 | 364 | +                                        {  | 
|---|
 | 365 | +                                                Interval total = visuals.getGlobalInterval();  | 
|---|
 | 366 | +                                                  | 
|---|
 | 367 | +                                                long t = start;  | 
|---|
 | 368 | +                                                start = end;  | 
|---|
 | 369 | +                                                end = t;  | 
|---|
 | 370 | +                                                  | 
|---|
 | 371 | +                                                // reversed  | 
|---|
 | 372 | +                                                long S = visuals.getTimeScale().getInterval().start;  | 
|---|
 | 373 | +                                                long E = visuals.getTimeScale().getInterval().end;  | 
|---|
 | 374 | +                                                  | 
|---|
 | 375 | +                                                // start*M + B = S  | 
|---|
 | 376 | +                                                // end*M + B = E  | 
|---|
 | 377 | +                                                double M = (double)(S - E) / (start - end);  | 
|---|
 | 378 | +                                                long B = E - (long)(end * M);  | 
|---|
 | 379 | +                                                start = (long)(S*M) + B;  | 
|---|
 | 380 | +                                                end = (long)(E*M) + B;  | 
|---|
 | 381 | +                                                if (start < total.start)  | 
|---|
 | 382 | +                                                {  | 
|---|
 | 383 | +                                                        start = total.start;  | 
|---|
 | 384 | +                                                }  | 
|---|
 | 385 | +                                                if (end > total.end)  | 
|---|
 | 386 | +                                                {  | 
|---|
 | 387 | +                                                        end = total.end;  | 
|---|
 | 388 | +                                                }  | 
|---|
 | 389 | +                                        }  | 
|---|
 | 390 | +                                          | 
|---|
 | 391 | +                                        moveTime(new Interval(start, end));  | 
|---|
 | 392 | +                                }  | 
|---|
 | 393 | +                        });  | 
|---|
 | 394 | +                          | 
|---|
 | 395 | +                        addMouseWheelListener(new MouseAdapter()  | 
|---|
 | 396 | +                        {  | 
|---|
 | 397 | +                                @Override  | 
|---|
 | 398 | +                                public void mouseWheelMoved(MouseWheelEvent e)  | 
|---|
 | 399 | +                                {  | 
|---|
 | 400 | +                                        //bar.setValue(bar.getValue() + e.getWheelRotation() * 10);  | 
|---|
 | 401 | +                                        visuals.scale *= Math.exp(e.getWheelRotation()/10.0);  | 
|---|
 | 402 | +                                        visuals.scale = visuals.layout();  | 
|---|
 | 403 | +                                        //System.out.println(visuals.scale);  | 
|---|
 | 404 | +                                        timelinePanel.drawVisualization();  | 
|---|
 | 405 | +                                        scroller.calibrate();  | 
|---|
 | 406 | +                                        repaint();  | 
|---|
 | 407 | +                                }  | 
|---|
 | 408 | +                        });  | 
|---|
 | 409 | +                }  | 
|---|
 | 410 | +  | 
|---|
 | 411 | +                public RoughTime getTime(Point p)  | 
|---|
 | 412 | +                {  | 
|---|
 | 413 | +                        TimeScale scale = visuals.getTimeScale();  | 
|---|
 | 414 | +                        long timestamp = scale.toTime(p.x);  | 
|---|
 | 415 | +                        return new RoughTime(timestamp, TimeUnit.DAY);  | 
|---|
 | 416 | +                }  | 
|---|
 | 417 | +  | 
|---|
 | 418 | +                protected void drawVisualization(Graphics2D g)  | 
|---|
 | 419 | +                {  | 
|---|
 | 420 | +                        if (g == null)  | 
|---|
 | 421 | +                        {  | 
|---|
 | 422 | +                                return;  | 
|---|
 | 423 | +                        }  | 
|---|
 | 424 | +                          | 
|---|
 | 425 | +                        g.setColor(Color.white);  | 
|---|
 | 426 | +                        g.fillRect(0, 0, 2 * getSize().width, getSize().height);  | 
|---|
 | 427 | +                        visuals.setBounds(0, 0, getSize().width, getSize().height);  | 
|---|
 | 428 | +                        objectLocations = new ArrayList<Mouseover>();  | 
|---|
 | 429 | +                        timeline.render(g, objectLocations);  | 
|---|
 | 430 | +                        grid.render(g, objectLocations);  | 
|---|
 | 431 | +                }  | 
|---|
 | 432 | +  | 
|---|
 | 433 | +                protected boolean paintOnTop(Graphics2D g, int w, int h)  | 
|---|
 | 434 | +                {  | 
|---|
 | 435 | +                        if (!mouseIsDown)  | 
|---|
 | 436 | +                        {  | 
|---|
 | 437 | +                                return false;  | 
|---|
 | 438 | +                        }  | 
|---|
 | 439 | +                          | 
|---|
 | 440 | +                        int a = mouse.x;  | 
|---|
 | 441 | +                        int b = firstMouse.x;  | 
|---|
 | 442 | +                        if (a > b)  | 
|---|
 | 443 | +                        {  | 
|---|
 | 444 | +                                g.setColor(new Color(255, 255, 120, 100));  | 
|---|
 | 445 | +                                g.fillRect(b, 0, a - b, h);  | 
|---|
 | 446 | +                        }  | 
|---|
 | 447 | +                        else  | 
|---|
 | 448 | +                        {  | 
|---|
 | 449 | +                                g.setColor(new Color(255, 120, 255, 100));  | 
|---|
 | 450 | +                                g.fillRect(a, 0, b - a, h);  | 
|---|
 | 451 | +                        }  | 
|---|
 | 452 | +                          | 
|---|
 | 453 | +                        g.setColor(Color.blue);  | 
|---|
 | 454 | +                        g.drawLine(a, 0, a, h);  | 
|---|
 | 455 | +                        g.drawLine(b, 0, b, h);  | 
|---|
 | 456 | +                        return true;  | 
|---|
 | 457 | +                }  | 
|---|
 | 458 | +        }  | 
|---|
 | 459 | +  | 
|---|
 | 460 | +        @Override  | 
|---|
 | 461 | +        public String getName()  | 
|---|
 | 462 | +        {  | 
|---|
 | 463 | +                return "Timeline";  | 
|---|
 | 464 | +        }  | 
|---|
 | 465 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.vis;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import java.awt.*;  | 
|---|
 | 4 | +import java.util.*;  | 
|---|
 | 5 | +  | 
|---|
 | 6 | +import timeflow.data.db.*;  | 
|---|
 | 7 | +import timeflow.model.Display;  | 
|---|
 | 8 | +import timeflow.model.VirtualField;  | 
|---|
 | 9 | +import timeflow.util.*;  | 
|---|
 | 10 | +  | 
|---|
 | 11 | +public class GroupVisualAct extends VisualAct  | 
|---|
 | 12 | +{  | 
|---|
 | 13 | +	private ArrayList<Act> group=new ArrayList<Act>();  | 
|---|
 | 14 | +	private boolean mixed=false;  | 
|---|
 | 15 | +	private DoubleBag<Color> colorBag;  | 
|---|
 | 16 | +	int numActs=0;  | 
|---|
 | 17 | +	double total=0;  | 
|---|
 | 18 | +	  | 
|---|
 | 19 | +	public GroupVisualAct(java.util.List<VisualAct> vacts, boolean mixed, Rectangle bounds)  | 
|---|
 | 20 | +	{  | 
|---|
 | 21 | +		super(vacts.get(0).act);  | 
|---|
 | 22 | +		int n=vacts.size();  | 
|---|
 | 23 | +		  | 
|---|
 | 24 | +		VisualAct proto=vacts.get(0);  | 
|---|
 | 25 | +		  | 
|---|
 | 26 | +		this.color=proto.color;  | 
|---|
 | 27 | +		this.trackString=proto.trackString;  | 
|---|
 | 28 | +		this.visible=proto.visible;  | 
|---|
 | 29 | +		this.x=proto.x;  | 
|---|
 | 30 | +		this.y=proto.y;  | 
|---|
 | 31 | +		  | 
|---|
 | 32 | +		this.spaceToRight=proto.spaceToRight;  | 
|---|
 | 33 | +		this.start=proto.start;  | 
|---|
 | 34 | +		this.group=new ArrayList<Act>();  | 
|---|
 | 35 | +		this.label="Group of "+n+" events";  | 
|---|
 | 36 | +		this.mouseOver=this.label;  | 
|---|
 | 37 | +		this.colorBag=new DoubleBag<Color>();  | 
|---|
 | 38 | +		Field sizeField=act.getDB().getField(VirtualField.SIZE);  | 
|---|
 | 39 | +		for (VisualAct v: vacts)  | 
|---|
 | 40 | +		{  | 
|---|
 | 41 | +			numActs++;  | 
|---|
 | 42 | +			if(sizeField!=null)  | 
|---|
 | 43 | +				total+=v.act.getValue(sizeField);  | 
|---|
 | 44 | +			this.size+=v.size;  | 
|---|
 | 45 | +			this.colorBag.add(v.color, v.size);  | 
|---|
 | 46 | +		}  | 
|---|
 | 47 | +		this.size=Math.sqrt(this.size);  | 
|---|
 | 48 | +		this.mixed=mixed;		  | 
|---|
 | 49 | +	}  | 
|---|
 | 50 | +  | 
|---|
 | 51 | +	public int getNumActs()  | 
|---|
 | 52 | +	{  | 
|---|
 | 53 | +		return numActs;  | 
|---|
 | 54 | +	}  | 
|---|
 | 55 | +	  | 
|---|
 | 56 | +	public void add(Act secondAct)  | 
|---|
 | 57 | +	{  | 
|---|
 | 58 | +		if (group==null)  | 
|---|
 | 59 | +		{  | 
|---|
 | 60 | +			group=new ArrayList<Act>();  | 
|---|
 | 61 | +			if (act!=null)  | 
|---|
 | 62 | +				group.add(act);  | 
|---|
 | 63 | +		}  | 
|---|
 | 64 | +		group.add(secondAct);  | 
|---|
 | 65 | +	}  | 
|---|
 | 66 | +  | 
|---|
 | 67 | +	public void draw(Graphics2D g, int ox, int oy, int r, Rectangle maxFill, boolean showDuration)  | 
|---|
 | 68 | +	{  | 
|---|
 | 69 | +		if (!mixed)  | 
|---|
 | 70 | +		{  | 
|---|
 | 71 | +			g.setColor(color);  | 
|---|
 | 72 | +			g.fillOval(ox,oy-r,2*r,2*r);  | 
|---|
 | 73 | +			g.drawOval(ox-2,oy-r-2,2*r+3,2*r+3);  | 
|---|
 | 74 | +		}  | 
|---|
 | 75 | +		else  | 
|---|
 | 76 | +		{  | 
|---|
 | 77 | +			java.util.List<Color> colors=colorBag.listTop(8, true);  | 
|---|
 | 78 | +			double total=0;  | 
|---|
 | 79 | +			for (Color c: colors)  | 
|---|
 | 80 | +				total+=colorBag.num(c);  | 
|---|
 | 81 | +							  | 
|---|
 | 82 | +			// now draw pie chart thing.  | 
|---|
 | 83 | +			double angle=0;  | 
|---|
 | 84 | +			int pieCenterX=ox+r;  | 
|---|
 | 85 | +			int pieCenterY=oy;  | 
|---|
 | 86 | +			for (Color c: colors)  | 
|---|
 | 87 | +			{  | 
|---|
 | 88 | +				double num=colorBag.num(c);  | 
|---|
 | 89 | +				double sa=(360*angle)/total;  | 
|---|
 | 90 | +				int startAngle=(int)(sa);  | 
|---|
 | 91 | +				int arcAngle=(int)(((360*(angle+num)))/total-sa);  | 
|---|
 | 92 | +				g.setColor(c);  | 
|---|
 | 93 | +				g.fillArc(pieCenterX-r,pieCenterY-r,2*r,2*r,startAngle,arcAngle);  | 
|---|
 | 94 | +				angle+=num;  | 
|---|
 | 95 | +			}		  | 
|---|
 | 96 | +		}  | 
|---|
 | 97 | +	}  | 
|---|
 | 98 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.vis;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.model.*;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +import java.awt.*;  | 
|---|
 | 6 | +import java.util.ArrayList;  | 
|---|
 | 7 | +  | 
|---|
 | 8 | +public class Mouseover extends Rectangle {  | 
|---|
 | 9 | +	public Object thing;  | 
|---|
 | 10 | +	public Mouseover(Object thing, int x, int y, int w, int h)  | 
|---|
 | 11 | +	{  | 
|---|
 | 12 | +		super(x,y,w,h);  | 
|---|
 | 13 | +		this.thing=thing;  | 
|---|
 | 14 | +	}  | 
|---|
 | 15 | +	  | 
|---|
 | 16 | +	public void draw(Graphics2D g, int maxW, int maxH, Display display)  | 
|---|
 | 17 | +	{  | 
|---|
 | 18 | +		g.setColor(new Color(0,53,153));  | 
|---|
 | 19 | +		g.setColor(new Color(255,255,0,100));  | 
|---|
 | 20 | +		g.fill(this);  | 
|---|
 | 21 | +	}  | 
|---|
 | 22 | +	  | 
|---|
 | 23 | +	protected void draw(Graphics2D g, int maxW, int maxH, Display display, ArrayList labels, int numLines)  | 
|---|
 | 24 | +	{  | 
|---|
 | 25 | +		if (labels==null || labels.size()==0)  | 
|---|
 | 26 | +			return;  | 
|---|
 | 27 | +		  | 
|---|
 | 28 | +		// draw a background box.  | 
|---|
 | 29 | +		  | 
|---|
 | 30 | +		// find max number of chars, very very roughly!  | 
|---|
 | 31 | +		int boxW=50;  | 
|---|
 | 32 | +		for (int i=0; i<labels.size(); i+=2)  | 
|---|
 | 33 | +		{  | 
|---|
 | 34 | +			if (labels.get(i) instanceof String[])  | 
|---|
 | 35 | +				boxW=300;  | 
|---|
 | 36 | +			else if (labels.get(i) instanceof String)  | 
|---|
 | 37 | +			{  | 
|---|
 | 38 | +				boxW=Math.max(boxW, 50+50*((String)labels.get(i)).length());  | 
|---|
 | 39 | +			}  | 
|---|
 | 40 | +		}	  | 
|---|
 | 41 | +		  | 
|---|
 | 42 | +		  | 
|---|
 | 43 | +		boxW=Math.min(350, boxW);  | 
|---|
 | 44 | +		int boxH=18*numLines+10;  | 
|---|
 | 45 | +		int mx=this.x+this.width+5;  | 
|---|
 | 46 | +		int my=this.y+this.height+35;		  | 
|---|
 | 47 | +		  | 
|---|
 | 48 | +		// put box in a place where it does not obscure the data  | 
|---|
 | 49 | +		// or go off screen.  | 
|---|
 | 50 | +		if (my+boxH>maxH-10)  | 
|---|
 | 51 | +		{  | 
|---|
 | 52 | +			my=Math.max(10,this.y-boxH-5);  | 
|---|
 | 53 | +		}  | 
|---|
 | 54 | +		if (mx+boxW>maxW-10)  | 
|---|
 | 55 | +		{  | 
|---|
 | 56 | +			mx=Math.max(10,this.x-boxW-10);  | 
|---|
 | 57 | +		}  | 
|---|
 | 58 | +		int ty=my;  | 
|---|
 | 59 | +		g.setColor(new Color(0,0,0,70));  | 
|---|
 | 60 | +		g.fillRoundRect(mx-11, my-16, boxW, boxH,12,12);  | 
|---|
 | 61 | +		g.setColor(Color.white);  | 
|---|
 | 62 | +		g.fillRoundRect(mx-15, my-20, boxW, boxH,12,12);  | 
|---|
 | 63 | +		g.setColor(Color.darkGray);  | 
|---|
 | 64 | +		g.drawRoundRect(mx-15, my-20, boxW, boxH,12,12);  | 
|---|
 | 65 | +		  | 
|---|
 | 66 | +		// finally, draw the darn labels.  | 
|---|
 | 67 | +		for (int i=0; i<labels.size(); i+=2)  | 
|---|
 | 68 | +		{  | 
|---|
 | 69 | +			g.setFont(display.bold());  | 
|---|
 | 70 | +			String field=(String)labels.get(i);  | 
|---|
 | 71 | +			g.drawString(field,mx,ty);  | 
|---|
 | 72 | +			int sw=display.boldFontMetrics().stringWidth(field);  | 
|---|
 | 73 | +			g.setFont(display.plain());  | 
|---|
 | 74 | +			Object o=labels.get(i+1);  | 
|---|
 | 75 | +			if (o instanceof String)  | 
|---|
 | 76 | +			{  | 
|---|
 | 77 | +				g.drawString((String)o,mx+sw+9,ty);  | 
|---|
 | 78 | +				ty+=18;  | 
|---|
 | 79 | +			}  | 
|---|
 | 80 | +			else  | 
|---|
 | 81 | +			{  | 
|---|
 | 82 | +				ArrayList<String> lines=(ArrayList<String>)o;  | 
|---|
 | 83 | +				int dx=sw+9;  | 
|---|
 | 84 | +				for (String line: lines)  | 
|---|
 | 85 | +				{  | 
|---|
 | 86 | +					g.drawString((String)line,mx+dx,ty);  | 
|---|
 | 87 | +					ty+=18;  | 
|---|
 | 88 | +					dx=0;  | 
|---|
 | 89 | +				}  | 
|---|
 | 90 | +				ty+=5;  | 
|---|
 | 91 | +			}  | 
|---|
 | 92 | +		}		  | 
|---|
 | 93 | +	}  | 
|---|
 | 94 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.vis;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import java.awt.Graphics2D;  | 
|---|
 | 4 | +import java.util.ArrayList;  | 
|---|
 | 5 | +  | 
|---|
 | 6 | +import timeflow.data.db.Act;  | 
|---|
 | 7 | +import timeflow.data.db.ActDB;  | 
|---|
 | 8 | +import timeflow.data.db.Field;  | 
|---|
 | 9 | +import timeflow.model.Display;  | 
|---|
 | 10 | +  | 
|---|
 | 11 | +public class MouseoverLabel extends Mouseover {  | 
|---|
 | 12 | +  | 
|---|
 | 13 | +	String label1, label2;  | 
|---|
 | 14 | +	  | 
|---|
 | 15 | +	public MouseoverLabel(String label1, String label2, int x, int y, int w, int h) {  | 
|---|
 | 16 | +		super(label1, x, y, w, h);  | 
|---|
 | 17 | +		this.label1=label1;  | 
|---|
 | 18 | +		this.label2=label2;  | 
|---|
 | 19 | +	}  | 
|---|
 | 20 | +  | 
|---|
 | 21 | +  | 
|---|
 | 22 | +	public void draw(Graphics2D g, int maxW, int maxH, Display display)  | 
|---|
 | 23 | +	{  | 
|---|
 | 24 | +		super.draw(g, maxW, maxH, display);  | 
|---|
 | 25 | +		ArrayList labels=new ArrayList();  | 
|---|
 | 26 | +		labels.add(label1);  | 
|---|
 | 27 | +		labels.add(label2);  | 
|---|
 | 28 | +		int numLines=1;  | 
|---|
 | 29 | +		draw(g, maxW, maxH, display, labels, numLines);  | 
|---|
 | 30 | +	}  | 
|---|
 | 31 | +}  | 
|---|
 | 32 | +  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.vis;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.data.db.*;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +import java.awt.*;  | 
|---|
 | 6 | +import java.util.*;  | 
|---|
 | 7 | +  | 
|---|
 | 8 | +public class TagVisualAct extends VisualAct {  | 
|---|
 | 9 | +	  | 
|---|
 | 10 | +	Color[] colors;  | 
|---|
 | 11 | +	private static Color[] nullColors={new Color(230,230,230)};  | 
|---|
 | 12 | +  | 
|---|
 | 13 | +	public TagVisualAct(Act act) {  | 
|---|
 | 14 | +		super(act);  | 
|---|
 | 15 | +	}  | 
|---|
 | 16 | +	  | 
|---|
 | 17 | +	public void setColors(Color[] colors)  | 
|---|
 | 18 | +	{  | 
|---|
 | 19 | +		this.colors=colors;  | 
|---|
 | 20 | +		this.color=colors.length>0 ? colors[0] : Color.gray;  | 
|---|
 | 21 | +	}  | 
|---|
 | 22 | +  | 
|---|
 | 23 | +	public void draw(Graphics2D g, int ox, int oy, int r, Rectangle maxFill, boolean showDuration)  | 
|---|
 | 24 | +	{  | 
|---|
 | 25 | +		if (colors==null)  | 
|---|
 | 26 | +		{  | 
|---|
 | 27 | +			super.draw(g, ox, oy, r, maxFill, showDuration);  | 
|---|
 | 28 | +			return;  | 
|---|
 | 29 | +		}  | 
|---|
 | 30 | +  | 
|---|
 | 31 | +		Color[] c= colors==null || colors.length==0 ? nullColors : colors;  | 
|---|
 | 32 | +		int tx=ox-r;  | 
|---|
 | 33 | +		int side=2*r;  | 
|---|
 | 34 | +		for (int i=0; i<c.length; i++)  | 
|---|
 | 35 | +		{  | 
|---|
 | 36 | +			g.setColor(c[i]);  | 
|---|
 | 37 | +			int y0=-5+(oy-r)+(2*side*i)/c.length;  | 
|---|
 | 38 | +			int y1=-5+(oy-r)+(2*side*(i+1))/c.length;  | 
|---|
 | 39 | +			if (size>=0)  | 
|---|
 | 40 | +				g.fillRect(tx,y0,side+2,y1-y0);  | 
|---|
 | 41 | +			else  | 
|---|
 | 42 | +				g.drawRect(tx,y0,side+2,y1-y0);  | 
|---|
 | 43 | +		}  | 
|---|
 | 44 | +		  | 
|---|
 | 45 | +		if (end!=null && showDuration)  | 
|---|
 | 46 | +		{  | 
|---|
 | 47 | +			int lineY=y+6;  | 
|---|
 | 48 | +			g.fillRect(getX(), lineY, getEndX()-getX(), 2);  | 
|---|
 | 49 | +			g.drawLine(getX(), lineY, getX(), lineY-4);  | 
|---|
 | 50 | +		}  | 
|---|
 | 51 | +	}  | 
|---|
 | 52 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.vis;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.data.time.*;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +import java.util.*;  | 
|---|
 | 6 | +  | 
|---|
 | 7 | +public class TimeScale  | 
|---|
 | 8 | +{  | 
|---|
 | 9 | +        private double low, high;  | 
|---|
 | 10 | +        private Interval interval;  | 
|---|
 | 11 | +  | 
|---|
 | 12 | +        public TimeScale()  | 
|---|
 | 13 | +        {  | 
|---|
 | 14 | +                low = 0;  | 
|---|
 | 15 | +                high = 100;  | 
|---|
 | 16 | +                interval = new Interval(new Date(0).getTime(), new Date().getTime());  | 
|---|
 | 17 | +        }  | 
|---|
 | 18 | +  | 
|---|
 | 19 | +        public Interval getInterval()  | 
|---|
 | 20 | +        {  | 
|---|
 | 21 | +                return interval;  | 
|---|
 | 22 | +        }  | 
|---|
 | 23 | +  | 
|---|
 | 24 | +        public void setNumberRange(double low, double high)  | 
|---|
 | 25 | +        {  | 
|---|
 | 26 | +                this.low = low;  | 
|---|
 | 27 | +                this.high = high;  | 
|---|
 | 28 | +        }  | 
|---|
 | 29 | +  | 
|---|
 | 30 | +        public void setDateRange(Interval t)  | 
|---|
 | 31 | +        {  | 
|---|
 | 32 | +                setDateRange(t.start, t.end);  | 
|---|
 | 33 | +        }  | 
|---|
 | 34 | +  | 
|---|
 | 35 | +        public void setDateRange(long first, long last)  | 
|---|
 | 36 | +        {  | 
|---|
 | 37 | +                interval.setTo(first, last);  | 
|---|
 | 38 | +        }  | 
|---|
 | 39 | +  | 
|---|
 | 40 | +        public boolean containsDate(long date)  | 
|---|
 | 41 | +        {  | 
|---|
 | 42 | +                return interval.contains(date);  | 
|---|
 | 43 | +        }  | 
|---|
 | 44 | +  | 
|---|
 | 45 | +        public boolean containsNum(double x)  | 
|---|
 | 46 | +        {  | 
|---|
 | 47 | +                return x >= low && x <= high;  | 
|---|
 | 48 | +        }  | 
|---|
 | 49 | +  | 
|---|
 | 50 | +        public long duration()  | 
|---|
 | 51 | +        {  | 
|---|
 | 52 | +                return interval.length();  | 
|---|
 | 53 | +        }  | 
|---|
 | 54 | +  | 
|---|
 | 55 | +        public double toNum(long time)  | 
|---|
 | 56 | +        {  | 
|---|
 | 57 | +                return low + (high - low) * (time - interval.start) / (double) duration();  | 
|---|
 | 58 | +        }  | 
|---|
 | 59 | +  | 
|---|
 | 60 | +        public long spaceToTime(double space)  | 
|---|
 | 61 | +        {  | 
|---|
 | 62 | +                return (long) (space * duration() / (high - low));  | 
|---|
 | 63 | +        }  | 
|---|
 | 64 | +  | 
|---|
 | 65 | +        public int toInt(long time)  | 
|---|
 | 66 | +        {  | 
|---|
 | 67 | +                return (int) toNum(time);  | 
|---|
 | 68 | +        }  | 
|---|
 | 69 | +  | 
|---|
 | 70 | +        public long toTime(double num)  | 
|---|
 | 71 | +        {  | 
|---|
 | 72 | +                double millis = interval.start + duration() * (num - low) / (high - low);  | 
|---|
 | 73 | +                return (long) millis;  | 
|---|
 | 74 | +        }  | 
|---|
 | 75 | +  | 
|---|
 | 76 | +        public double getLow()  | 
|---|
 | 77 | +        {  | 
|---|
 | 78 | +                return low;  | 
|---|
 | 79 | +        }  | 
|---|
 | 80 | +  | 
|---|
 | 81 | +        public void setLow(double low)  | 
|---|
 | 82 | +        {  | 
|---|
 | 83 | +                this.low = low;  | 
|---|
 | 84 | +        }  | 
|---|
 | 85 | +  | 
|---|
 | 86 | +        public double getHigh()  | 
|---|
 | 87 | +        {  | 
|---|
 | 88 | +                return high;  | 
|---|
 | 89 | +        }  | 
|---|
 | 90 | +  | 
|---|
 | 91 | +        public void setHigh(double high)  | 
|---|
 | 92 | +        {  | 
|---|
 | 93 | +                this.high = high;  | 
|---|
 | 94 | +        }  | 
|---|
 | 95 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.vis;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.data.db.*;  | 
|---|
 | 4 | +import timeflow.data.time.*;  | 
|---|
 | 5 | +import timeflow.model.*;  | 
|---|
 | 6 | +import timeflow.vis.timeline.TimelineTrack;  | 
|---|
 | 7 | +  | 
|---|
 | 8 | +import java.awt.*;  | 
|---|
 | 9 | +import java.util.*;  | 
|---|
 | 10 | +  | 
|---|
 | 11 | +import timeflow.util.*;  | 
|---|
 | 12 | +  | 
|---|
 | 13 | +public class VisualAct implements Comparable  | 
|---|
 | 14 | +{  | 
|---|
 | 15 | +  | 
|---|
 | 16 | +        Color color;  | 
|---|
 | 17 | +        String label;  | 
|---|
 | 18 | +        String mouseOver;  | 
|---|
 | 19 | +        double size = 1;  | 
|---|
 | 20 | +        String trackString;  | 
|---|
 | 21 | +        TimelineTrack track;  | 
|---|
 | 22 | +        boolean visible;  | 
|---|
 | 23 | +        Act act;  | 
|---|
 | 24 | +        int x, y;  | 
|---|
 | 25 | +        int spaceToRight;  | 
|---|
 | 26 | +        RoughTime start, end;  | 
|---|
 | 27 | +        int endX;  | 
|---|
 | 28 | +  | 
|---|
 | 29 | +        public VisualAct(Act act)  | 
|---|
 | 30 | +        {  | 
|---|
 | 31 | +                this.act = act;  | 
|---|
 | 32 | +        }  | 
|---|
 | 33 | +  | 
|---|
 | 34 | +        public int getX()  | 
|---|
 | 35 | +        {  | 
|---|
 | 36 | +                return x;  | 
|---|
 | 37 | +        }  | 
|---|
 | 38 | +  | 
|---|
 | 39 | +        public int getDotR()  | 
|---|
 | 40 | +        {  | 
|---|
 | 41 | +                return Math.max(1, (int) Math.abs(size));  | 
|---|
 | 42 | +        }  | 
|---|
 | 43 | +  | 
|---|
 | 44 | +        public void setX(int x)  | 
|---|
 | 45 | +        {  | 
|---|
 | 46 | +                this.x = x;  | 
|---|
 | 47 | +        }  | 
|---|
 | 48 | +  | 
|---|
 | 49 | +        public int getY()  | 
|---|
 | 50 | +        {  | 
|---|
 | 51 | +                return y;  | 
|---|
 | 52 | +        }  | 
|---|
 | 53 | +  | 
|---|
 | 54 | +        public void setY(int y)  | 
|---|
 | 55 | +        {  | 
|---|
 | 56 | +                this.y = y;  | 
|---|
 | 57 | +        }  | 
|---|
 | 58 | +  | 
|---|
 | 59 | +        public void draw(Graphics2D g, int ox, int oy, int r, Rectangle maxFill, boolean showDuration)  | 
|---|
 | 60 | +        {  | 
|---|
 | 61 | +                g.setColor(getColor());  | 
|---|
 | 62 | +                if (size >= 0)  | 
|---|
 | 63 | +                {  | 
|---|
 | 64 | +                        // Slow!  | 
|---|
 | 65 | +                        g.fillOval(ox, y - r, 2 * r, 2 * r);  | 
|---|
 | 66 | +                } else  | 
|---|
 | 67 | +                {  | 
|---|
 | 68 | +                        // Slow!  | 
|---|
 | 69 | +                        g.drawOval(ox, y - r, 2 * r, 2 * r);  | 
|---|
 | 70 | +                }  | 
|---|
 | 71 | +                if (end != null && showDuration)  | 
|---|
 | 72 | +                {  | 
|---|
 | 73 | +                        int lineY = y + 6;  | 
|---|
 | 74 | +                        g.fillRect(getX(), lineY, getEndX() - getX(), 2);  | 
|---|
 | 75 | +                        g.drawLine(getX(), lineY, getX(), lineY - 4);  | 
|---|
 | 76 | +                }  | 
|---|
 | 77 | +  | 
|---|
 | 78 | +        }  | 
|---|
 | 79 | +  | 
|---|
 | 80 | +        public Mouseover draw(Graphics2D g, Rectangle maxFill, Rectangle bounds,  | 
|---|
 | 81 | +                Display display, boolean text, boolean showDuration)  | 
|---|
 | 82 | +        {  | 
|---|
 | 83 | +                if (!isVisible())  | 
|---|
 | 84 | +                {  | 
|---|
 | 85 | +                        return null;  | 
|---|
 | 86 | +                }  | 
|---|
 | 87 | +  | 
|---|
 | 88 | +                if (x > bounds.x + bounds.width && (end == null || endX > bounds.x + bounds.width)  | 
|---|
 | 89 | +                        || x < bounds.x - 200 && (end == null || endX < bounds.x - 200))  | 
|---|
 | 90 | +                {  | 
|---|
 | 91 | +                        return null;  | 
|---|
 | 92 | +                }  | 
|---|
 | 93 | +  | 
|---|
 | 94 | +                g.setFont(display.plain());  | 
|---|
 | 95 | +  | 
|---|
 | 96 | +                int r = getDotR();  | 
|---|
 | 97 | +                if (r <= 0)  | 
|---|
 | 98 | +                {  | 
|---|
 | 99 | +                        r = 1;  | 
|---|
 | 100 | +                }  | 
|---|
 | 101 | +                if (r > 30)  | 
|---|
 | 102 | +                {  | 
|---|
 | 103 | +                        r = 30;  | 
|---|
 | 104 | +                }  | 
|---|
 | 105 | +                int ox = text ? x - 2 * r : x;  | 
|---|
 | 106 | +  | 
|---|
 | 107 | +                draw(g, ox, y - 2, r, maxFill, showDuration);  | 
|---|
 | 108 | +  | 
|---|
 | 109 | +  | 
|---|
 | 110 | +                if (!text)  | 
|---|
 | 111 | +                {  | 
|---|
 | 112 | +                        return new VisualActMouseover(ox - 2, y - r - 4, 4 + 2 * r, 4 + 2 * r);  | 
|---|
 | 113 | +                }  | 
|---|
 | 114 | +  | 
|---|
 | 115 | +                int labelSpace = getSpaceToRight() - 12;  | 
|---|
 | 116 | +                int sw = 0;  | 
|---|
 | 117 | +                if (labelSpace > 50)  | 
|---|
 | 118 | +                {  | 
|---|
 | 119 | +                        String s = display.format(getLabel(), labelSpace / 8, true);  | 
|---|
 | 120 | +                        int n = s.indexOf(' ');  | 
|---|
 | 121 | +                        int tx = x + 5;  | 
|---|
 | 122 | +                        int ty = y + 4;  | 
|---|
 | 123 | +                        if (n < 1)  | 
|---|
 | 124 | +                        {  | 
|---|
 | 125 | +                                g.drawString(s, tx, ty);  | 
|---|
 | 126 | +                        } else  | 
|---|
 | 127 | +                        {  | 
|---|
 | 128 | +                                String first = s.substring(0, n);  | 
|---|
 | 129 | +                                g.drawString(first, tx, ty);  | 
|---|
 | 130 | +                                Color c = ColorUtils.interpolate(g.getColor(), Color.white, .33);  | 
|---|
 | 131 | +                                g.setColor(c);  | 
|---|
 | 132 | +                                g.drawString(s.substring(n), tx + display.plainFontMetrics().stringWidth(first), ty);  | 
|---|
 | 133 | +                        }  | 
|---|
 | 134 | +                        sw = display.plainFontMetrics().stringWidth(s);  | 
|---|
 | 135 | +                }  | 
|---|
 | 136 | +  | 
|---|
 | 137 | +                return new VisualActMouseover(x - 3 - 2 * r, y - r - 8, 14 + sw, r + 13 + 2 * r);  | 
|---|
 | 138 | +        }  | 
|---|
 | 139 | +  | 
|---|
 | 140 | +        public Act getAct()  | 
|---|
 | 141 | +        {  | 
|---|
 | 142 | +                return act;  | 
|---|
 | 143 | +        }  | 
|---|
 | 144 | +  | 
|---|
 | 145 | +        public boolean isVisible()  | 
|---|
 | 146 | +        {  | 
|---|
 | 147 | +                return visible;  | 
|---|
 | 148 | +        }  | 
|---|
 | 149 | +  | 
|---|
 | 150 | +        public void setVisible(boolean visible)  | 
|---|
 | 151 | +        {  | 
|---|
 | 152 | +                this.visible = visible;  | 
|---|
 | 153 | +        }  | 
|---|
 | 154 | +  | 
|---|
 | 155 | +        public RoughTime getStart()  | 
|---|
 | 156 | +        {  | 
|---|
 | 157 | +                return start;  | 
|---|
 | 158 | +        }  | 
|---|
 | 159 | +  | 
|---|
 | 160 | +        public void setStart(RoughTime start)  | 
|---|
 | 161 | +        {  | 
|---|
 | 162 | +                this.start = start;  | 
|---|
 | 163 | +        }  | 
|---|
 | 164 | +  | 
|---|
 | 165 | +        public Color getColor()  | 
|---|
 | 166 | +        {  | 
|---|
 | 167 | +                return color;  | 
|---|
 | 168 | +        }  | 
|---|
 | 169 | +  | 
|---|
 | 170 | +        public void setColor(Color color)  | 
|---|
 | 171 | +        {  | 
|---|
 | 172 | +                this.color = color;  | 
|---|
 | 173 | +        }  | 
|---|
 | 174 | +  | 
|---|
 | 175 | +        public String getLabel()  | 
|---|
 | 176 | +        {  | 
|---|
 | 177 | +                return label;  | 
|---|
 | 178 | +        }  | 
|---|
 | 179 | +  | 
|---|
 | 180 | +        public void setLabel(String label)  | 
|---|
 | 181 | +        {  | 
|---|
 | 182 | +                this.label = label;  | 
|---|
 | 183 | +        }  | 
|---|
 | 184 | +  | 
|---|
 | 185 | +        public String getMouseOver()  | 
|---|
 | 186 | +        {  | 
|---|
 | 187 | +                return mouseOver;  | 
|---|
 | 188 | +        }  | 
|---|
 | 189 | +  | 
|---|
 | 190 | +        public void setMouseOver(String mouseOver)  | 
|---|
 | 191 | +        {  | 
|---|
 | 192 | +                this.mouseOver = mouseOver;  | 
|---|
 | 193 | +        }  | 
|---|
 | 194 | +  | 
|---|
 | 195 | +        public double getSize()  | 
|---|
 | 196 | +        {  | 
|---|
 | 197 | +                return size;  | 
|---|
 | 198 | +        }  | 
|---|
 | 199 | +  | 
|---|
 | 200 | +        public void setSize(double size)  | 
|---|
 | 201 | +        {  | 
|---|
 | 202 | +                this.size = size;  | 
|---|
 | 203 | +        }  | 
|---|
 | 204 | +  | 
|---|
 | 205 | +        public String getTrackString()  | 
|---|
 | 206 | +        {  | 
|---|
 | 207 | +                return trackString == null ? "" : trackString;  | 
|---|
 | 208 | +        }  | 
|---|
 | 209 | +  | 
|---|
 | 210 | +        public void setTrackString(String track)  | 
|---|
 | 211 | +        {  | 
|---|
 | 212 | +                this.trackString = track;  | 
|---|
 | 213 | +        }  | 
|---|
 | 214 | +  | 
|---|
 | 215 | +        public TimelineTrack getTrack()  | 
|---|
 | 216 | +        {  | 
|---|
 | 217 | +                return track;  | 
|---|
 | 218 | +        }  | 
|---|
 | 219 | +  | 
|---|
 | 220 | +        public void setTrack(TimelineTrack track)  | 
|---|
 | 221 | +        {  | 
|---|
 | 222 | +                this.track = track;  | 
|---|
 | 223 | +        }  | 
|---|
 | 224 | +  | 
|---|
 | 225 | +        public void setSpaceToRight(int spaceToRight)  | 
|---|
 | 226 | +        {  | 
|---|
 | 227 | +                this.spaceToRight = spaceToRight;  | 
|---|
 | 228 | +        }  | 
|---|
 | 229 | +  | 
|---|
 | 230 | +        public int getSpaceToRight()  | 
|---|
 | 231 | +        {  | 
|---|
 | 232 | +                return spaceToRight;  | 
|---|
 | 233 | +        }  | 
|---|
 | 234 | +  | 
|---|
 | 235 | +        public int getEndX()  | 
|---|
 | 236 | +        {  | 
|---|
 | 237 | +                return endX;  | 
|---|
 | 238 | +        }  | 
|---|
 | 239 | +  | 
|---|
 | 240 | +        public void setEndX(int endX)  | 
|---|
 | 241 | +        {  | 
|---|
 | 242 | +                this.endX = endX;  | 
|---|
 | 243 | +        }  | 
|---|
 | 244 | +  | 
|---|
 | 245 | +        public RoughTime getEnd()  | 
|---|
 | 246 | +        {  | 
|---|
 | 247 | +                return end;  | 
|---|
 | 248 | +        }  | 
|---|
 | 249 | +  | 
|---|
 | 250 | +        public void setEnd(RoughTime end)  | 
|---|
 | 251 | +        {  | 
|---|
 | 252 | +                this.end = end;  | 
|---|
 | 253 | +        }  | 
|---|
 | 254 | +  | 
|---|
 | 255 | +        @Override  | 
|---|
 | 256 | +        public int compareTo(Object o)  | 
|---|
 | 257 | +        {  | 
|---|
 | 258 | +                return RoughTime.compare(start, ((VisualAct) o).start);  | 
|---|
 | 259 | +                //start.compareTo(((VisualAct)o).start);  | 
|---|
 | 260 | +        }  | 
|---|
 | 261 | +  | 
|---|
 | 262 | +        class VisualActMouseover extends Mouseover  | 
|---|
 | 263 | +        {  | 
|---|
 | 264 | +  | 
|---|
 | 265 | +                public VisualActMouseover(int x, int y, int w, int h)  | 
|---|
 | 266 | +                {  | 
|---|
 | 267 | +                        super(VisualAct.this, x, y, w, h);  | 
|---|
 | 268 | +                }  | 
|---|
 | 269 | +  | 
|---|
 | 270 | +                public void draw(Graphics2D g, int maxW, int maxH, Display display)  | 
|---|
 | 271 | +                {  | 
|---|
 | 272 | +                        super.draw(g, maxW, maxH, display);  | 
|---|
 | 273 | +                        Act a = getAct();  | 
|---|
 | 274 | +                        ActDB db = a.getDB();  | 
|---|
 | 275 | +                        java.util.List<Field> fields = db.getFields();  | 
|---|
 | 276 | +                        ArrayList labels = new ArrayList();  | 
|---|
 | 277 | +                        int charWidth = 40;  | 
|---|
 | 278 | +                        int numLines = 1;  | 
|---|
 | 279 | +                        if (VisualAct.this instanceof GroupVisualAct)  | 
|---|
 | 280 | +                        {  | 
|---|
 | 281 | +                                GroupVisualAct gv = (GroupVisualAct) VisualAct.this;  | 
|---|
 | 282 | +                                labels.add(gv.getNumActs() + "");  | 
|---|
 | 283 | +                                labels.add("items");  | 
|---|
 | 284 | +                                Field sizeField = db.getField(VirtualField.SIZE);  | 
|---|
 | 285 | +                                if (sizeField != null)  | 
|---|
 | 286 | +                                {  | 
|---|
 | 287 | +                                        labels.add("Total " + sizeField.getName());  | 
|---|
 | 288 | +                                        double t = ((GroupVisualAct) (VisualAct.this)).total;  | 
|---|
 | 289 | +                                        labels.add(Display.format(t));  | 
|---|
 | 290 | +                                        numLines++;  | 
|---|
 | 291 | +                                }  | 
|---|
 | 292 | +                        } else  | 
|---|
 | 293 | +                        {  | 
|---|
 | 294 | +                                for (Field f : fields)  | 
|---|
 | 295 | +                                {  | 
|---|
 | 296 | +                                        labels.add(f.getName());  | 
|---|
 | 297 | +                                        Object val = a.get(f);  | 
|---|
 | 298 | +                                        String valString = display.toString(val);  | 
|---|
 | 299 | +                                        if (f.getName().length() + valString.length() + 2 > charWidth)  | 
|---|
 | 300 | +                                        {  | 
|---|
 | 301 | +                                                ArrayList<String> lines = Display.breakLines(valString, charWidth, 2 + f.getName().length());  | 
|---|
 | 302 | +                                                labels.add(lines);  | 
|---|
 | 303 | +                                                numLines += lines.size() + 1;  | 
|---|
 | 304 | +                                        } else  | 
|---|
 | 305 | +                                        {  | 
|---|
 | 306 | +                                                labels.add(valString);  | 
|---|
 | 307 | +                                                numLines++;  | 
|---|
 | 308 | +                                        }  | 
|---|
 | 309 | +                                }  | 
|---|
 | 310 | +                        }  | 
|---|
 | 311 | +                        draw(g, maxW, maxH, display, labels, numLines);  | 
|---|
 | 312 | +                }  | 
|---|
 | 313 | +        }  | 
|---|
 | 314 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.vis;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import java.awt.Color;  | 
|---|
 | 4 | +import java.awt.Rectangle;  | 
|---|
 | 5 | +import java.util.ArrayList;  | 
|---|
 | 6 | +import java.util.Collection;  | 
|---|
 | 7 | +import java.util.HashMap;  | 
|---|
 | 8 | +  | 
|---|
 | 9 | +import timeflow.data.db.Act;  | 
|---|
 | 10 | +import timeflow.data.db.ActDB;  | 
|---|
 | 11 | +import timeflow.data.db.ActList;  | 
|---|
 | 12 | +import timeflow.data.db.Field;  | 
|---|
 | 13 | +import timeflow.model.TFModel;  | 
|---|
 | 14 | +import timeflow.model.VirtualField;  | 
|---|
 | 15 | +  | 
|---|
 | 16 | +public class VisualActFactory  | 
|---|
 | 17 | +{  | 
|---|
 | 18 | +        // create one VisualAct per Act  | 
|---|
 | 19 | +  | 
|---|
 | 20 | +        private static java.util.List<VisualAct> create(ActList acts)  | 
|---|
 | 21 | +        {  | 
|---|
 | 22 | +                java.util.List<VisualAct> list = new ArrayList<VisualAct>();  | 
|---|
 | 23 | +                for (Act a : acts)  | 
|---|
 | 24 | +                {  | 
|---|
 | 25 | +                        VisualAct v = new TagVisualAct(a);  | 
|---|
 | 26 | +                        list.add(v);  | 
|---|
 | 27 | +                }  | 
|---|
 | 28 | +                return list;  | 
|---|
 | 29 | +        }  | 
|---|
 | 30 | +  | 
|---|
 | 31 | +        // create one VisualAct per Act/tag combo.  | 
|---|
 | 32 | +        public static java.util.List<VisualAct> create(ActList acts, Field tagField, boolean multipleColors)  | 
|---|
 | 33 | +        {  | 
|---|
 | 34 | +                if (tagField == null || tagField.getType() == String.class)  | 
|---|
 | 35 | +                {  | 
|---|
 | 36 | +                        return create(acts);  | 
|---|
 | 37 | +                }  | 
|---|
 | 38 | +                java.util.List<VisualAct> list = new ArrayList<VisualAct>();  | 
|---|
 | 39 | +                for (Act a : acts)  | 
|---|
 | 40 | +                {  | 
|---|
 | 41 | +                        String[] tags = a.getTextList(tagField);  | 
|---|
 | 42 | +                        if (tags == null || tags.length < 2)  | 
|---|
 | 43 | +                        {  | 
|---|
 | 44 | +                                VisualAct v = new TagVisualAct(a);  | 
|---|
 | 45 | +                                if (tags != null && tags.length == 1)  | 
|---|
 | 46 | +                                {  | 
|---|
 | 47 | +                                        v.setTrackString(tags[0]);  | 
|---|
 | 48 | +                                }  | 
|---|
 | 49 | +                                list.add(v);  | 
|---|
 | 50 | +                        } else  | 
|---|
 | 51 | +                        {  | 
|---|
 | 52 | +                                for (String tag : tags)  | 
|---|
 | 53 | +                                {  | 
|---|
 | 54 | +                                        VisualAct v = multipleColors ? new TagVisualAct(a) : new VisualAct(a);  | 
|---|
 | 55 | +                                        v.setTrackString(tag);  | 
|---|
 | 56 | +                                        list.add(v);  | 
|---|
 | 57 | +                                }  | 
|---|
 | 58 | +                        }  | 
|---|
 | 59 | +                }  | 
|---|
 | 60 | +                return list;  | 
|---|
 | 61 | +        }  | 
|---|
 | 62 | +  | 
|---|
 | 63 | +        public static Collection<VisualAct> makeEmFit(TFModel model, ArrayList<VisualAct> vacts, Rectangle bounds)  | 
|---|
 | 64 | +        {  | 
|---|
 | 65 | +                // Does everything fit? Because, if so, we're already good to go.  | 
|---|
 | 66 | +                int area = bounds.width * bounds.height;  | 
|---|
 | 67 | +                int room = area / 200;  | 
|---|
 | 68 | +                if (vacts.size() <= room)  | 
|---|
 | 69 | +                {  | 
|---|
 | 70 | +                        return vacts;  | 
|---|
 | 71 | +                }  | 
|---|
 | 72 | +  | 
|---|
 | 73 | +                ArrayList<VisualAct> results = new ArrayList<VisualAct>();  | 
|---|
 | 74 | +  | 
|---|
 | 75 | +                // OK. If:  | 
|---|
 | 76 | +                //     * there's room for more than one item, and    | 
|---|
 | 77 | +                //     * there's more than one color in use,  | 
|---|
 | 78 | +                //  | 
|---|
 | 79 | +                // Then let's see how many colors there are. Maybe we can do one bubble per color.  | 
|---|
 | 80 | +                ActDB db = model.getDB();  | 
|---|
 | 81 | +                if (room > 1 && (db.getField(VirtualField.COLOR) != null || db.getField(VirtualField.TRACK) != null))  | 
|---|
 | 82 | +                {  | 
|---|
 | 83 | +                        HashMap<Color, ArrayList<VisualAct>> colorGroupings = new HashMap<Color, ArrayList<VisualAct>>();  | 
|---|
 | 84 | +                        for (VisualAct v : vacts)  | 
|---|
 | 85 | +                        {  | 
|---|
 | 86 | +                                Color c = v.color;  | 
|---|
 | 87 | +                                ArrayList<VisualAct> grouping = colorGroupings.get(c);  | 
|---|
 | 88 | +                                if (grouping == null)  | 
|---|
 | 89 | +                                {  | 
|---|
 | 90 | +                                        grouping = new ArrayList<VisualAct>();  | 
|---|
 | 91 | +                                        colorGroupings.put(c, grouping);  | 
|---|
 | 92 | +                                }  | 
|---|
 | 93 | +                                grouping.add(v);  | 
|---|
 | 94 | +                        }  | 
|---|
 | 95 | +  | 
|---|
 | 96 | +                        if (colorGroupings.size() <= room) // Great! The colors fit. We now return one group VisualAct per color.  | 
|---|
 | 97 | +                        {  | 
|---|
 | 98 | +                                for (Color c : colorGroupings.keySet())  | 
|---|
 | 99 | +                                {  | 
|---|
 | 100 | +                                        ArrayList<VisualAct> grouping = colorGroupings.get(c);  | 
|---|
 | 101 | +                                        if (grouping.size() == 1)  | 
|---|
 | 102 | +                                        {  | 
|---|
 | 103 | +                                                results.add(grouping.get(0));  | 
|---|
 | 104 | +                                        } else if (grouping.size() > 1)  | 
|---|
 | 105 | +                                        {  | 
|---|
 | 106 | +                                                results.add(new GroupVisualAct(grouping, false, bounds));  | 
|---|
 | 107 | +                                        }  | 
|---|
 | 108 | +                                }  | 
|---|
 | 109 | +                                return results;  | 
|---|
 | 110 | +                        }  | 
|---|
 | 111 | +                }  | 
|---|
 | 112 | +  | 
|---|
 | 113 | +                // OK, too bad, even that doesn't fit. We will just create one fat VisualAct  | 
|---|
 | 114 | +                // that descibes the aggregate. C'est la vie!  | 
|---|
 | 115 | +  | 
|---|
 | 116 | +                results.add(new GroupVisualAct(vacts, true, bounds));  | 
|---|
 | 117 | +                return results;  | 
|---|
 | 118 | +        }  | 
|---|
 | 119 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.vis;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.data.db.*;  | 
|---|
 | 4 | +import timeflow.model.*;  | 
|---|
 | 5 | +  | 
|---|
 | 6 | +import java.awt.Color;  | 
|---|
 | 7 | +import java.util.*;  | 
|---|
 | 8 | +  | 
|---|
 | 9 | +public class VisualEncoder  | 
|---|
 | 10 | +{  | 
|---|
 | 11 | +  | 
|---|
 | 12 | +        private TFModel model;  | 
|---|
 | 13 | +        private java.util.List<VisualAct> visualActs = new ArrayList<VisualAct>();  | 
|---|
 | 14 | +        private double maxSize = 0;  | 
|---|
 | 15 | +  | 
|---|
 | 16 | +        public VisualEncoder(TFModel model)  | 
|---|
 | 17 | +        {  | 
|---|
 | 18 | +                this.model = model;  | 
|---|
 | 19 | +        }  | 
|---|
 | 20 | +  | 
|---|
 | 21 | +        public java.util.List<VisualAct> getVisualActs()  | 
|---|
 | 22 | +        {  | 
|---|
 | 23 | +                return visualActs;  | 
|---|
 | 24 | +        }  | 
|---|
 | 25 | +  | 
|---|
 | 26 | +        public void createVisualActs()  | 
|---|
 | 27 | +        {  | 
|---|
 | 28 | +                Field colorField = model.getDB().getField(VirtualField.COLOR);  | 
|---|
 | 29 | +                Field trackField = model.getDB().getField(VirtualField.TRACK);  | 
|---|
 | 30 | +                boolean multipleColors = colorField != null && colorField.getType() == String[].class && colorField != trackField;  | 
|---|
 | 31 | +                visualActs = VisualActFactory.create(model.getDB().all(), trackField, multipleColors);  | 
|---|
 | 32 | +                Collections.sort(visualActs);  | 
|---|
 | 33 | +        }  | 
|---|
 | 34 | +  | 
|---|
 | 35 | +        public List<VisualAct> apply()  | 
|---|
 | 36 | +        {  | 
|---|
 | 37 | +  | 
|---|
 | 38 | +                ActList visibleActs = model.getActs();  | 
|---|
 | 39 | +                Field start = model.getDB().getField(VirtualField.START);  | 
|---|
 | 40 | +                Field end = model.getDB().getField(VirtualField.END);  | 
|---|
 | 41 | +                Field size = model.getDB().getField(VirtualField.SIZE);  | 
|---|
 | 42 | +                if (size != null)  | 
|---|
 | 43 | +                {  | 
|---|
 | 44 | +                        double[] minmax = DBUtils.minmax(model.getActs(), size);  | 
|---|
 | 45 | +                        maxSize = Math.max(Math.abs(minmax[0]), Math.abs(minmax[1]));  | 
|---|
 | 46 | +                }  | 
|---|
 | 47 | +  | 
|---|
 | 48 | +                // apply color, label, visibility, etc.  | 
|---|
 | 49 | +                for (VisualAct v : visualActs)  | 
|---|
 | 50 | +                {  | 
|---|
 | 51 | +                        Act a = v.getAct();  | 
|---|
 | 52 | +                        v.setStart(start == null ? null : a.getTime(start));  | 
|---|
 | 53 | +                        v.setEnd(end == null ? null : a.getTime(end));  | 
|---|
 | 54 | +                        v.setVisible(visibleActs.contains(a) && v.getStart() != null && v.getStart().isDefined());  | 
|---|
 | 55 | +                        apply(v);  | 
|---|
 | 56 | +                }  | 
|---|
 | 57 | +  | 
|---|
 | 58 | +                return visualActs;  | 
|---|
 | 59 | +        }  | 
|---|
 | 60 | +  | 
|---|
 | 61 | +        public void apply(VisualAct v)  | 
|---|
 | 62 | +        {  | 
|---|
 | 63 | +                Display display = model.getDisplay();  | 
|---|
 | 64 | +                ActDB db = model.getDB();  | 
|---|
 | 65 | +                Act a = v.getAct();  | 
|---|
 | 66 | +                Field label = db.getField(VirtualField.LABEL);  | 
|---|
 | 67 | +                Field track = db.getField(VirtualField.TRACK);  | 
|---|
 | 68 | +                Field color = db.getField(VirtualField.COLOR);  | 
|---|
 | 69 | +                Field size = db.getField(VirtualField.SIZE);  | 
|---|
 | 70 | +  | 
|---|
 | 71 | +                if (label == null)  | 
|---|
 | 72 | +                {  | 
|---|
 | 73 | +                        v.setLabel("");  | 
|---|
 | 74 | +                } else  | 
|---|
 | 75 | +                {  | 
|---|
 | 76 | +                        v.setLabel(a.getString(label));  | 
|---|
 | 77 | +                }  | 
|---|
 | 78 | +  | 
|---|
 | 79 | +                double s = Display.MAX_DOT_SIZE;  | 
|---|
 | 80 | +                if (size == null || maxSize == 0)  | 
|---|
 | 81 | +                {  | 
|---|
 | 82 | +                        v.setSize(s / 3);  | 
|---|
 | 83 | +                } else  | 
|---|
 | 84 | +                {  | 
|---|
 | 85 | +                        double z = s * Math.sqrt(Math.abs(a.getValue(size)) / maxSize);  | 
|---|
 | 86 | +                        if (a.getValue(size) < 0)  | 
|---|
 | 87 | +                        {  | 
|---|
 | 88 | +                                z = -z;  | 
|---|
 | 89 | +                        }  | 
|---|
 | 90 | +                        v.setSize(z);  | 
|---|
 | 91 | +                }  | 
|---|
 | 92 | +  | 
|---|
 | 93 | +                // For setting the track:  | 
|---|
 | 94 | +                // This is a little complicated, but if the track is set to  | 
|---|
 | 95 | +                // tags (that is, track.getType()==String[].class) then  | 
|---|
 | 96 | +                // the track string was set earlier so it doesn't need to be set now.  | 
|---|
 | 97 | +                if (track == null)  | 
|---|
 | 98 | +                {  | 
|---|
 | 99 | +                        v.setTrackString("");  | 
|---|
 | 100 | +                } else if (track.getType() == String.class)  | 
|---|
 | 101 | +                {  | 
|---|
 | 102 | +                        v.setTrackString(a.getString(track));  | 
|---|
 | 103 | +                }  | 
|---|
 | 104 | +  | 
|---|
 | 105 | +                if (color == null || color == track)  | 
|---|
 | 106 | +                {  | 
|---|
 | 107 | +                        if (track == null)  | 
|---|
 | 108 | +                        {  | 
|---|
 | 109 | +                                v.setColor(display.getColor("timeline.unspecified.color"));  | 
|---|
 | 110 | +                        } else  | 
|---|
 | 111 | +                        {  | 
|---|
 | 112 | +                                v.setColor(display.makeColor(v.getTrackString()));  | 
|---|
 | 113 | +                        }  | 
|---|
 | 114 | +                } else  | 
|---|
 | 115 | +                {  | 
|---|
 | 116 | +                        if (color.getType() == String[].class)  | 
|---|
 | 117 | +                        {  | 
|---|
 | 118 | +                                String[] tags = a.getTextList(color);  | 
|---|
 | 119 | +                                if (tags == null || tags.length == 0)  | 
|---|
 | 120 | +                                {  | 
|---|
 | 121 | +                                        ((TagVisualAct) v).setColors(new Color[0]);  | 
|---|
 | 122 | +                                } else  | 
|---|
 | 123 | +                                {  | 
|---|
 | 124 | +                                        int n = tags.length;  | 
|---|
 | 125 | +                                        Color[] c = new Color[n];  | 
|---|
 | 126 | +                                        for (int i = 0; i < n; i++)  | 
|---|
 | 127 | +                                        {  | 
|---|
 | 128 | +                                                c[i] = display.makeColor(tags[i]);  | 
|---|
 | 129 | +                                        }  | 
|---|
 | 130 | +                                        ((TagVisualAct) v).setColors(c);  | 
|---|
 | 131 | +                                }  | 
|---|
 | 132 | +                        } else  | 
|---|
 | 133 | +                        {  | 
|---|
 | 134 | +                                v.setColor(display.makeColor(a.getString(color)));  | 
|---|
 | 135 | +                        }  | 
|---|
 | 136 | +                }  | 
|---|
 | 137 | +        }  | 
|---|
 | 138 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.vis.calendar;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import java.awt.*;  | 
|---|
 | 4 | +import java.util.Collection;  | 
|---|
 | 5 | +  | 
|---|
 | 6 | +  | 
|---|
 | 7 | +import timeflow.model.*;  | 
|---|
 | 8 | +import timeflow.vis.*;  | 
|---|
 | 9 | +import timeflow.data.time.*;  | 
|---|
 | 10 | +import timeflow.data.db.*;  | 
|---|
 | 11 | +  | 
|---|
 | 12 | +public class CalendarVisuals {  | 
|---|
 | 13 | +	VisualEncoder encoder;  | 
|---|
 | 14 | +	TFModel model;  | 
|---|
 | 15 | +	Rectangle bounds=new Rectangle();  | 
|---|
 | 16 | +	public Grid grid;  | 
|---|
 | 17 | +	  | 
|---|
 | 18 | +	public enum Layout {DAY, WEEK, MONTH, YEAR};  | 
|---|
 | 19 | +	private Layout layoutStyle=Layout.DAY;  | 
|---|
 | 20 | +	  | 
|---|
 | 21 | +	public enum DrawStyle {LABEL, ICON};  | 
|---|
 | 22 | +	DrawStyle drawStyle=DrawStyle.ICON;  | 
|---|
 | 23 | +	  | 
|---|
 | 24 | +	public enum FitStyle {LOOSE, TIGHT};  | 
|---|
 | 25 | +	FitStyle fitStyle=FitStyle.LOOSE;  | 
|---|
 | 26 | +	  | 
|---|
 | 27 | +	public CalendarVisuals(TFModel model)  | 
|---|
 | 28 | +	{  | 
|---|
 | 29 | +		this.model=model;  | 
|---|
 | 30 | +		encoder=new VisualEncoder(model);  | 
|---|
 | 31 | +	}	  | 
|---|
 | 32 | +	  | 
|---|
 | 33 | +	public void render(Graphics2D g, Collection<Mouseover> objectLocations)  | 
|---|
 | 34 | +	{  | 
|---|
 | 35 | +		grid.render(g, model.getDisplay(), bounds, this, objectLocations);  | 
|---|
 | 36 | +	}	  | 
|---|
 | 37 | +	  | 
|---|
 | 38 | +	public Interval getGlobalInterval()  | 
|---|
 | 39 | +	{  | 
|---|
 | 40 | +		return DBUtils.range(model.getDB().all(), VirtualField.START);  | 
|---|
 | 41 | +	}  | 
|---|
 | 42 | +	  | 
|---|
 | 43 | +	public Rectangle getBounds() {  | 
|---|
 | 44 | +		return bounds;  | 
|---|
 | 45 | +	}  | 
|---|
 | 46 | +  | 
|---|
 | 47 | +	public void setBounds(int x, int y, int w, int h) {  | 
|---|
 | 48 | +		bounds.setBounds(x,y,w,h);  | 
|---|
 | 49 | +		if (grid==null)  | 
|---|
 | 50 | +			makeGrid(true);  | 
|---|
 | 51 | +	}  | 
|---|
 | 52 | +	  | 
|---|
 | 53 | +	public void setDrawStyle(DrawStyle drawStyle)  | 
|---|
 | 54 | +	{  | 
|---|
 | 55 | +		this.drawStyle=drawStyle;  | 
|---|
 | 56 | +	}  | 
|---|
 | 57 | +	  | 
|---|
 | 58 | +	public void setLayoutStyle(Layout layoutStyle)  | 
|---|
 | 59 | +	{  | 
|---|
 | 60 | +		this.layoutStyle=layoutStyle;  | 
|---|
 | 61 | +		makeGrid(true);  | 
|---|
 | 62 | +	}  | 
|---|
 | 63 | +	  | 
|---|
 | 64 | +	public void setFitStyle(FitStyle fitStyle)  | 
|---|
 | 65 | +	{  | 
|---|
 | 66 | +		this.fitStyle=fitStyle;  | 
|---|
 | 67 | +		makeGrid(true);  | 
|---|
 | 68 | +	}  | 
|---|
 | 69 | +	  | 
|---|
 | 70 | +	public void makeGrid(boolean fresh)  | 
|---|
 | 71 | +	{  | 
|---|
 | 72 | +		Interval interval=getGlobalInterval();  | 
|---|
 | 73 | +		int dy=0;  | 
|---|
 | 74 | +		if (grid!=null)  | 
|---|
 | 75 | +			dy=grid.dy;  | 
|---|
 | 76 | +		switch(layoutStyle)  | 
|---|
 | 77 | +		{  | 
|---|
 | 78 | +			case DAY: grid=new Grid(TimeUnit.WEEK, TimeUnit.DAY_OF_WEEK, interval); break;  | 
|---|
 | 79 | +			case WEEK: grid=new Grid(TimeUnit.multipleWeeks(8), TimeUnit.WEEK, interval); break;  | 
|---|
 | 80 | +			case MONTH: grid=new Grid(TimeUnit.YEAR, TimeUnit.MONTH, interval); break;  | 
|---|
 | 81 | +			case YEAR: grid=new Grid(TimeUnit.DECADE, TimeUnit.YEAR, interval);   | 
|---|
 | 82 | +		}		  | 
|---|
 | 83 | +		grid.makeCells(encoder.getVisualActs());  | 
|---|
 | 84 | +		if (!fresh)  | 
|---|
 | 85 | +			grid.dy=dy;  | 
|---|
 | 86 | +	}  | 
|---|
 | 87 | +  | 
|---|
 | 88 | +	public void init()  | 
|---|
 | 89 | +	{  | 
|---|
 | 90 | +		note(new TFEvent(TFEvent.Type.DATABASE_CHANGE,null));  | 
|---|
 | 91 | +	}  | 
|---|
 | 92 | +	  | 
|---|
 | 93 | +	public void initAllButGrid()  | 
|---|
 | 94 | +	{  | 
|---|
 | 95 | +		encoder.createVisualActs();  | 
|---|
 | 96 | +		encoder.apply();  | 
|---|
 | 97 | +	}  | 
|---|
 | 98 | +	  | 
|---|
 | 99 | +	public void note(TFEvent e) {  | 
|---|
 | 100 | +		if (e.affectsRowSet())  | 
|---|
 | 101 | +		{  | 
|---|
 | 102 | +			encoder.createVisualActs();  | 
|---|
 | 103 | +		}  | 
|---|
 | 104 | +  | 
|---|
 | 105 | +		updateVisuals(e.affectsRowSet());  | 
|---|
 | 106 | +	}  | 
|---|
 | 107 | +		  | 
|---|
 | 108 | +	public void updateVisuals(boolean fresh)  | 
|---|
 | 109 | +	{  | 
|---|
 | 110 | +		encoder.apply();  | 
|---|
 | 111 | +		makeGrid(fresh);  | 
|---|
 | 112 | +	}  | 
|---|
 | 113 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.vis.calendar;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.data.time.*;  | 
|---|
 | 4 | +import timeflow.model.Display;  | 
|---|
 | 5 | +import timeflow.vis.*;  | 
|---|
 | 6 | +  | 
|---|
 | 7 | +import java.util.*;  | 
|---|
 | 8 | +import java.awt.*;  | 
|---|
 | 9 | +import java.text.*;  | 
|---|
 | 10 | +  | 
|---|
 | 11 | +public class Grid  | 
|---|
 | 12 | +{  | 
|---|
 | 13 | +  | 
|---|
 | 14 | +        TimeUnit rowUnit, columnUnit;  | 
|---|
 | 15 | +        RoughTime startRow, endRow;  | 
|---|
 | 16 | +        Interval interval;  | 
|---|
 | 17 | +        HashMap<Long, GridCell> cells;  | 
|---|
 | 18 | +        int[] screenGridX;  | 
|---|
 | 19 | +        int cellHeight = 80, cellWidth, numCols, numRows;  | 
|---|
 | 20 | +        Rectangle bounds = new Rectangle();  | 
|---|
 | 21 | +        int dy;  | 
|---|
 | 22 | +        static final DateFormat dayOfWeek = new SimpleDateFormat("EEE");  | 
|---|
 | 23 | +        static final DateFormat month = new SimpleDateFormat("MMM d");  | 
|---|
 | 24 | +        static final String[] day =  | 
|---|
 | 25 | +        {  | 
|---|
 | 26 | +                "SUN", "MON", "TUES", "WED", "THURS", "FRI", "SAT"  | 
|---|
 | 27 | +        };  | 
|---|
 | 28 | +  | 
|---|
 | 29 | +        Grid(TimeUnit rowUnit, TimeUnit columnUnit, Interval interval)  | 
|---|
 | 30 | +        {  | 
|---|
 | 31 | +                this.rowUnit = rowUnit;  | 
|---|
 | 32 | +                this.columnUnit = columnUnit;  | 
|---|
 | 33 | +                numCols = columnUnit.numUnitsIn(rowUnit);  | 
|---|
 | 34 | +                setInterval(interval);  | 
|---|
 | 35 | +        }  | 
|---|
 | 36 | +  | 
|---|
 | 37 | +        public void setDY(int dy)  | 
|---|
 | 38 | +        {  | 
|---|
 | 39 | +                this.dy = dy;  | 
|---|
 | 40 | +        }  | 
|---|
 | 41 | +  | 
|---|
 | 42 | +        public int getCalendarHeight()  | 
|---|
 | 43 | +        {  | 
|---|
 | 44 | +                return bounds.height + bounds.y + 20;  | 
|---|
 | 45 | +        }  | 
|---|
 | 46 | +  | 
|---|
 | 47 | +        public RoughTime getTime(int x, int y)  | 
|---|
 | 48 | +        {  | 
|---|
 | 49 | +                y += dy;  | 
|---|
 | 50 | +                if (!bounds.contains(x, y))  | 
|---|
 | 51 | +                {  | 
|---|
 | 52 | +                        return null;  | 
|---|
 | 53 | +                }  | 
|---|
 | 54 | +  | 
|---|
 | 55 | +                // find grid coordinates.  | 
|---|
 | 56 | +                int gridX = (x - bounds.x) / cellWidth;  | 
|---|
 | 57 | +                int gridY = (y - bounds.y) / cellHeight;  | 
|---|
 | 58 | +  | 
|---|
 | 59 | +                return startRow.plus(rowUnit, gridY).plus(columnUnit, gridX);  | 
|---|
 | 60 | +        }  | 
|---|
 | 61 | +  | 
|---|
 | 62 | +        public double getScrollFraction()  | 
|---|
 | 63 | +        {  | 
|---|
 | 64 | +                double x = (getFirstDrawnTime().getTime() - startRow.getTime()) / (double) (endRow.getTime() - startRow.getTime());  | 
|---|
 | 65 | +                if (x < 0)  | 
|---|
 | 66 | +                {  | 
|---|
 | 67 | +                        return 0;  | 
|---|
 | 68 | +                }  | 
|---|
 | 69 | +                if (x > 1)  | 
|---|
 | 70 | +                {  | 
|---|
 | 71 | +                        return 1;  | 
|---|
 | 72 | +                }  | 
|---|
 | 73 | +                return x;  | 
|---|
 | 74 | +        }  | 
|---|
 | 75 | +  | 
|---|
 | 76 | +        public RoughTime getFirstDrawnTime()  | 
|---|
 | 77 | +        {  | 
|---|
 | 78 | +                int gridY = (dy - bounds.y) / cellHeight;  | 
|---|
 | 79 | +  | 
|---|
 | 80 | +                return startRow.plus(rowUnit, gridY);  | 
|---|
 | 81 | +        }  | 
|---|
 | 82 | +  | 
|---|
 | 83 | +        private Point getGridCorner(long timestamp)  | 
|---|
 | 84 | +        {  | 
|---|
 | 85 | +                int diff = (int) columnUnit.difference(timestamp, startRow.getTime());  | 
|---|
 | 86 | +                int gridX = diff % numCols;  | 
|---|
 | 87 | +                int gridY = diff / numCols;  | 
|---|
 | 88 | +                return new Point(gridX, gridY);  | 
|---|
 | 89 | +        }  | 
|---|
 | 90 | +  | 
|---|
 | 91 | +        public Rectangle getCell(long timestamp)  | 
|---|
 | 92 | +        {  | 
|---|
 | 93 | +                Point p = getGridCorner(timestamp);  | 
|---|
 | 94 | +                return new Rectangle(bounds.x + p.x * cellWidth, bounds.y + p.y * cellHeight - dy,  | 
|---|
 | 95 | +                        cellWidth, cellHeight);  | 
|---|
 | 96 | +        }  | 
|---|
 | 97 | +  | 
|---|
 | 98 | +        void setInterval(Interval interval)  | 
|---|
 | 99 | +        {  | 
|---|
 | 100 | +                this.interval = interval;  | 
|---|
 | 101 | +                startRow = rowUnit.roundDown(interval.start);  | 
|---|
 | 102 | +                endRow = rowUnit.roundDown(interval.end);  | 
|---|
 | 103 | +                numRows = 1 + (int) (rowUnit.difference(endRow.getTime(), startRow.getTime()));  | 
|---|
 | 104 | +  | 
|---|
 | 105 | +                // the next line fixes a problem with multi-century data sets.  | 
|---|
 | 106 | +                // it works, but there's probably a better way to do this :-)  | 
|---|
 | 107 | +                if (numRows > 50 && rowUnit.getRoughSize() >= TimeUnit.YEAR.getRoughSize())  | 
|---|
 | 108 | +                {  | 
|---|
 | 109 | +                        numRows++;  | 
|---|
 | 110 | +                }  | 
|---|
 | 111 | +        }  | 
|---|
 | 112 | +  | 
|---|
 | 113 | +        void makeCells(java.util.List<VisualAct> visualActs)  | 
|---|
 | 114 | +        {  | 
|---|
 | 115 | +                cells = new HashMap<Long, GridCell>();  | 
|---|
 | 116 | +                for (VisualAct v : visualActs)  | 
|---|
 | 117 | +                {  | 
|---|
 | 118 | +                        if (v.getStart() == null)  | 
|---|
 | 119 | +                        {  | 
|---|
 | 120 | +                                continue;  | 
|---|
 | 121 | +                        }  | 
|---|
 | 122 | +                        long timestamp = v.getStart().getTime();  | 
|---|
 | 123 | +                        RoughTime timeKey = columnUnit.roundDown(timestamp);  | 
|---|
 | 124 | +                        GridCell cell = cells.get(timeKey.getTime());  | 
|---|
 | 125 | +                        if (cell == null)  | 
|---|
 | 126 | +                        {  | 
|---|
 | 127 | +                                cell = new GridCell(timeKey);  | 
|---|
 | 128 | +                                cells.put(timeKey.getTime(), cell);  | 
|---|
 | 129 | +                                Point p = getGridCorner(timestamp);  | 
|---|
 | 130 | +                                cell.gridX = p.x;  | 
|---|
 | 131 | +                                cell.gridY = p.y;  | 
|---|
 | 132 | +                        }  | 
|---|
 | 133 | +                        cell.visualActs.add(v);  | 
|---|
 | 134 | +                }  | 
|---|
 | 135 | +        }  | 
|---|
 | 136 | +  | 
|---|
 | 137 | +        void render(Graphics2D g, Display display, Rectangle screenBounds, CalendarVisuals visuals,  | 
|---|
 | 138 | +                Collection<Mouseover> objectLocations)  | 
|---|
 | 139 | +        {  | 
|---|
 | 140 | +                g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);  | 
|---|
 | 141 | +                int left = 110, right = 20;  | 
|---|
 | 142 | +                int padY = 50;  | 
|---|
 | 143 | +                boolean shouldLabel = visuals.drawStyle == CalendarVisuals.DrawStyle.LABEL;  | 
|---|
 | 144 | +  | 
|---|
 | 145 | +                cellWidth = (screenBounds.width - left - right) / numCols;  | 
|---|
 | 146 | +  | 
|---|
 | 147 | +                boolean fitTight = visuals.fitStyle == CalendarVisuals.FitStyle.TIGHT;  | 
|---|
 | 148 | +                int idealHeight = fitTight ? 12 : Display.CALENDAR_CELL_HEIGHT;  | 
|---|
 | 149 | +                cellHeight = Math.max(idealHeight, (screenBounds.height - padY - 10) / numRows);  | 
|---|
 | 150 | +                this.bounds.setBounds(left, padY, numCols * cellWidth, numRows * cellHeight);  | 
|---|
 | 151 | +  | 
|---|
 | 152 | +                g.setColor(new Color(240, 240, 240));//Color.white);  | 
|---|
 | 153 | +                g.fill(screenBounds);  | 
|---|
 | 154 | +                g.setColor(new Color(245, 245, 245));  | 
|---|
 | 155 | +                g.drawRect(bounds.x, bounds.y - dy, bounds.width, bounds.height);  | 
|---|
 | 156 | +                g.setFont(display.bold());  | 
|---|
 | 157 | +  | 
|---|
 | 158 | +                // draw vertical grid lines.  | 
|---|
 | 159 | +                Color gridColor = new Color(220, 220, 220);  | 
|---|
 | 160 | +  | 
|---|
 | 161 | +                for (int i = 0; i <= numCols; i++)  | 
|---|
 | 162 | +                {  | 
|---|
 | 163 | +                        int x = bounds.x + i * cellWidth;  | 
|---|
 | 164 | +                        g.setColor(gridColor);  | 
|---|
 | 165 | +                        g.drawLine(x, bounds.y - dy, x, bounds.y + bounds.height - dy);  | 
|---|
 | 166 | +                        if (rowUnit == TimeUnit.WEEK && i < 7)  | 
|---|
 | 167 | +                        {  | 
|---|
 | 168 | +                                g.setColor(Color.gray);  | 
|---|
 | 169 | +                                g.drawString(day[i], x, bounds.y - dy - 6);  | 
|---|
 | 170 | +                        }  | 
|---|
 | 171 | +                }  | 
|---|
 | 172 | +  | 
|---|
 | 173 | +                // horizontal grid lines.  | 
|---|
 | 174 | +                RoughTime labelTime = startRow.copy();  | 
|---|
 | 175 | +                int lastLabelY = -100;  | 
|---|
 | 176 | +                int lastYear = -1000000;  | 
|---|
 | 177 | +                FontMetrics fm = display.boldFontMetrics();  | 
|---|
 | 178 | +                int skipped = 0;  | 
|---|
 | 179 | +                for (int i = 0; i < numRows; i++)  | 
|---|
 | 180 | +                {  | 
|---|
 | 181 | +                        int y = bounds.y + i * cellHeight;  | 
|---|
 | 182 | +                        if (y - dy > -50)  | 
|---|
 | 183 | +                        {  | 
|---|
 | 184 | +                                if (skipped > 0)  | 
|---|
 | 185 | +                                {  | 
|---|
 | 186 | +                                        rowUnit.addTo(labelTime, skipped);  | 
|---|
 | 187 | +                                        skipped = 0;  | 
|---|
 | 188 | +                                }  | 
|---|
 | 189 | +                                g.setColor(gridColor);  | 
|---|
 | 190 | +                                g.drawLine(bounds.x, y - dy, bounds.x + bounds.width, y - dy);  | 
|---|
 | 191 | +                                if (y - lastLabelY > 30 || lastLabelY < 0)  | 
|---|
 | 192 | +                                {  | 
|---|
 | 193 | +                                        String label = null;  | 
|---|
 | 194 | +                                        if (rowUnit == TimeUnit.WEEK)  | 
|---|
 | 195 | +                                        {  | 
|---|
 | 196 | +                                                int year = TimeUtils.cal(labelTime.getTime()).get(Calendar.YEAR);  | 
|---|
 | 197 | +                                                if (year != lastYear)  | 
|---|
 | 198 | +                                                {  | 
|---|
 | 199 | +                                                        label = labelTime.format();  | 
|---|
 | 200 | +                                                } else  | 
|---|
 | 201 | +                                                {  | 
|---|
 | 202 | +                                                        label = month.format(labelTime.toDate());  | 
|---|
 | 203 | +                                                }  | 
|---|
 | 204 | +                                                lastYear = year;  | 
|---|
 | 205 | +                                        } else  | 
|---|
 | 206 | +                                        {  | 
|---|
 | 207 | +                                                label = labelTime.format();  | 
|---|
 | 208 | +                                        }  | 
|---|
 | 209 | +                                        g.setColor(Color.gray);  | 
|---|
 | 210 | +                                        g.drawString(label, bounds.x - fm.stringWidth(label) - 15, y + 15 - dy);  | 
|---|
 | 211 | +                                        lastLabelY = y;  | 
|---|
 | 212 | +                                }  | 
|---|
 | 213 | +                                if (y - dy > screenBounds.height)  | 
|---|
 | 214 | +                                {  | 
|---|
 | 215 | +                                        break;  | 
|---|
 | 216 | +                                }  | 
|---|
 | 217 | +  | 
|---|
 | 218 | +                                // now draw, in gray, the labels for each of the boxes.  | 
|---|
 | 219 | +                                if (!fitTight)  | 
|---|
 | 220 | +                                {  | 
|---|
 | 221 | +                                        RoughTime gridLabel = labelTime.copy();  | 
|---|
 | 222 | +                                        int labelH = 13;  | 
|---|
 | 223 | +                                        for (int j = 0; j < numCols; j++)  | 
|---|
 | 224 | +                                        {  | 
|---|
 | 225 | +  | 
|---|
 | 226 | +                                                g.setColor(Color.gray);  | 
|---|
 | 227 | +                                                g.setFont(display.bold());  | 
|---|
 | 228 | +                                                String label = columnUnit.format(gridLabel.toDate());  | 
|---|
 | 229 | +                                                g.drawString(label, bounds.x + j * cellWidth + 3, y - dy + labelH);  | 
|---|
 | 230 | +                                                columnUnit.addTo(gridLabel);  | 
|---|
 | 231 | +                                        }  | 
|---|
 | 232 | +                                }  | 
|---|
 | 233 | +                                rowUnit.addTo(labelTime);  | 
|---|
 | 234 | +                        } else  | 
|---|
 | 235 | +                        {  | 
|---|
 | 236 | +                                skipped++;  | 
|---|
 | 237 | +                        }  | 
|---|
 | 238 | +                }  | 
|---|
 | 239 | +  | 
|---|
 | 240 | +                // draw a frame around the whole thing.  | 
|---|
 | 241 | +                g.setColor(Color.darkGray);  | 
|---|
 | 242 | +                g.drawRect(bounds.x, bounds.y - dy, bounds.width, bounds.height);  | 
|---|
 | 243 | +  | 
|---|
 | 244 | +                // draw backgrounds  | 
|---|
 | 245 | +                for (GridCell cell : cells.values())  | 
|---|
 | 246 | +                {  | 
|---|
 | 247 | +                        // are any visible?  | 
|---|
 | 248 | +                        boolean visible = false;  | 
|---|
 | 249 | +                        for (VisualAct v : cell.visualActs)  | 
|---|
 | 250 | +                        {  | 
|---|
 | 251 | +                                if (v.isVisible())  | 
|---|
 | 252 | +                                {  | 
|---|
 | 253 | +                                        visible = true;  | 
|---|
 | 254 | +                                        break;  | 
|---|
 | 255 | +                                }  | 
|---|
 | 256 | +                        }  | 
|---|
 | 257 | +                        int cx = bounds.x + cell.gridX * cellWidth;  | 
|---|
 | 258 | +                        int cy = bounds.y + cell.gridY * cellHeight - dy;  | 
|---|
 | 259 | +  | 
|---|
 | 260 | +                        if (cy < screenBounds.y - 50 || cy > screenBounds.y + screenBounds.height + 50)  | 
|---|
 | 261 | +                        {  | 
|---|
 | 262 | +                                continue;  | 
|---|
 | 263 | +                        }  | 
|---|
 | 264 | +  | 
|---|
 | 265 | +                        // label top of cell.  | 
|---|
 | 266 | +                        int labelH = 0;  | 
|---|
 | 267 | +                        g.setColor(new Color(240, 240, 240));  | 
|---|
 | 268 | +  | 
|---|
 | 269 | +                        if (visible)  | 
|---|
 | 270 | +                        {  | 
|---|
 | 271 | +                                g.setColor(Color.white);  | 
|---|
 | 272 | +                                g.fillRect(cx + 1, cy + 1, cellWidth - 1, cellHeight - 1);  | 
|---|
 | 273 | +                        }  | 
|---|
 | 274 | +                        if (cellHeight > 42)  | 
|---|
 | 275 | +                        {  | 
|---|
 | 276 | +                                labelH = 13;  | 
|---|
 | 277 | +                                g.setColor(Color.darkGray);  | 
|---|
 | 278 | +                                g.setFont(display.bold());  | 
|---|
 | 279 | +                                String label = columnUnit.format(cell.time.toDate());  | 
|---|
 | 280 | +                                g.drawString(label, cx + 3, cy + labelH);  | 
|---|
 | 281 | +                        }  | 
|---|
 | 282 | +  | 
|---|
 | 283 | +                }  | 
|---|
 | 284 | +  | 
|---|
 | 285 | +  | 
|---|
 | 286 | +  | 
|---|
 | 287 | +                // draw items.  | 
|---|
 | 288 | +                int mx = 10, my = shouldLabel ? 18 : 10;  | 
|---|
 | 289 | +                for (GridCell cell : cells.values())  | 
|---|
 | 290 | +                {  | 
|---|
 | 291 | +  | 
|---|
 | 292 | +                        int cx = bounds.x + cell.gridX * cellWidth;  | 
|---|
 | 293 | +                        int cy = bounds.y + cell.gridY * cellHeight - dy;  | 
|---|
 | 294 | +  | 
|---|
 | 295 | +                        if (cy < screenBounds.y - 50 || cy > screenBounds.y + screenBounds.height + 50)  | 
|---|
 | 296 | +                        {  | 
|---|
 | 297 | +                                continue;  | 
|---|
 | 298 | +                        }  | 
|---|
 | 299 | +  | 
|---|
 | 300 | +                        // label top of cell.  | 
|---|
 | 301 | +                        int labelH = cellHeight > 42 ? 13 : 0;  | 
|---|
 | 302 | +  | 
|---|
 | 303 | +                        // now draw the items in the cell.  | 
|---|
 | 304 | +                        // old, non-aggregation code:  | 
|---|
 | 305 | +  | 
|---|
 | 306 | +                        // START AGGREGATION CODE  | 
|---|
 | 307 | +  | 
|---|
 | 308 | +                        ArrayList<VisualAct> visibleActs = new ArrayList<VisualAct>();  | 
|---|
 | 309 | +                        for (VisualAct v : cell.visualActs)  | 
|---|
 | 310 | +                        {  | 
|---|
 | 311 | +                                if (v.isVisible())  | 
|---|
 | 312 | +                                {  | 
|---|
 | 313 | +                                        visibleActs.add(v);  | 
|---|
 | 314 | +                                }  | 
|---|
 | 315 | +                        }  | 
|---|
 | 316 | +                        Iterator<VisualAct> vacts =  | 
|---|
 | 317 | +                                VisualActFactory.makeEmFit(visuals.model, visibleActs, new Rectangle(cx, cy, cellWidth, cellHeight)).iterator();  | 
|---|
 | 318 | +  | 
|---|
 | 319 | +                        // END AGGREGATION CODE  | 
|---|
 | 320 | +  | 
|---|
 | 321 | +                        int leftX = 6;  | 
|---|
 | 322 | +                        int cdx = leftX;  | 
|---|
 | 323 | +                        int topDotY = Math.min(labelH + 16, cellHeight / 2);  | 
|---|
 | 324 | +                        int cdy = topDotY;  | 
|---|
 | 325 | +                        while (vacts.hasNext())  | 
|---|
 | 326 | +                        {  | 
|---|
 | 327 | +                                VisualAct v = vacts.next();  | 
|---|
 | 328 | +                                if (!v.isVisible())  | 
|---|
 | 329 | +                                {  | 
|---|
 | 330 | +                                        continue;  | 
|---|
 | 331 | +                                }  | 
|---|
 | 332 | +  | 
|---|
 | 333 | +                                // set x,y, room to right.  | 
|---|
 | 334 | +                                int x = cx + cdx;  | 
|---|
 | 335 | +                                int y = cy + cdy;  | 
|---|
 | 336 | +  | 
|---|
 | 337 | +                                int space = cellWidth - 20;  | 
|---|
 | 338 | +                                v.setX(x);  | 
|---|
 | 339 | +                                v.setY(y);  | 
|---|
 | 340 | +                                v.setSpaceToRight(space);  | 
|---|
 | 341 | +                                Mouseover o = v.draw(g, new Rectangle(cx + 1, cy + labelH + 1, cellWidth - 2, cellHeight - 2 - labelH),  | 
|---|
 | 342 | +                                        bounds, display, shouldLabel, false);  | 
|---|
 | 343 | +                                if (o != null)  | 
|---|
 | 344 | +                                {  | 
|---|
 | 345 | +                                        objectLocations.add(o);  | 
|---|
 | 346 | +                                }  | 
|---|
 | 347 | +  | 
|---|
 | 348 | +                                // go to next location. if we're labeling, we do this vertically.  | 
|---|
 | 349 | +                                // otherwise, left-to-right, then top-to-bottom.  | 
|---|
 | 350 | +  | 
|---|
 | 351 | +                                if (shouldLabel)  | 
|---|
 | 352 | +                                {  | 
|---|
 | 353 | +                                        cdy += my;  | 
|---|
 | 354 | +                                        if (cdy > cellHeight - 2 - my)  | 
|---|
 | 355 | +                                        {  | 
|---|
 | 356 | +                                                g.drawString("...", x, y + my);  | 
|---|
 | 357 | +                                                break;  | 
|---|
 | 358 | +                                        }  | 
|---|
 | 359 | +                                } else  | 
|---|
 | 360 | +                                {  | 
|---|
 | 361 | +                                        cdx += mx;  | 
|---|
 | 362 | +                                        if (cdx > cellWidth - mx / 2 - 2 && vacts.hasNext())  | 
|---|
 | 363 | +                                        {  | 
|---|
 | 364 | +                                                cdx = leftX;  | 
|---|
 | 365 | +                                                cdy += my;  | 
|---|
 | 366 | +                                                if (cdy > cellHeight - my / 2)  | 
|---|
 | 367 | +                                                {  | 
|---|
 | 368 | +                                                        break;  | 
|---|
 | 369 | +                                                }  | 
|---|
 | 370 | +                                        }  | 
|---|
 | 371 | +                                }  | 
|---|
 | 372 | +                        }  | 
|---|
 | 373 | +                }  | 
|---|
 | 374 | +        }  | 
|---|
 | 375 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.vis.calendar;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +  | 
|---|
 | 4 | +import timeflow.vis.*;  | 
|---|
 | 5 | +import timeflow.data.time.*;  | 
|---|
 | 6 | +  | 
|---|
 | 7 | +import java.util.*;  | 
|---|
 | 8 | +import java.awt.*;  | 
|---|
 | 9 | +  | 
|---|
 | 10 | +public class GridCell {  | 
|---|
 | 11 | +	ArrayList<VisualAct> visualActs=new ArrayList<VisualAct>();  | 
|---|
 | 12 | +	Rectangle bounds;  | 
|---|
 | 13 | +	RoughTime time;  | 
|---|
 | 14 | +	int gridX, gridY;  | 
|---|
 | 15 | +	  | 
|---|
 | 16 | +	GridCell(RoughTime time)  | 
|---|
 | 17 | +	{  | 
|---|
 | 18 | +		this.time=time;  | 
|---|
 | 19 | +	}  | 
|---|
 | 20 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.vis.timeline;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import java.awt.*;  | 
|---|
 | 4 | +import java.util.*;  | 
|---|
 | 5 | +  | 
|---|
 | 6 | +import timeflow.data.time.Interval;  | 
|---|
 | 7 | +import timeflow.data.time.TimeUtils;  | 
|---|
 | 8 | +import timeflow.model.*;  | 
|---|
 | 9 | +import timeflow.vis.Mouseover;  | 
|---|
 | 10 | +import timeflow.vis.TimeScale;  | 
|---|
 | 11 | +  | 
|---|
 | 12 | +public class AxisRenderer {  | 
|---|
 | 13 | +	  | 
|---|
 | 14 | +	TimelineVisuals visuals;  | 
|---|
 | 15 | +	  | 
|---|
 | 16 | +	public AxisRenderer(TimelineVisuals visuals)  | 
|---|
 | 17 | +	{  | 
|---|
 | 18 | +		this.visuals=visuals;  | 
|---|
 | 19 | +	}  | 
|---|
 | 20 | +	  | 
|---|
 | 21 | +	public void render(Graphics2D g, Collection<Mouseover> objectLocations)  | 
|---|
 | 22 | +	{		  | 
|---|
 | 23 | +		TFModel model=visuals.getModel();  | 
|---|
 | 24 | +		g.setColor(model.getDisplay().getColor("chart.background"));  | 
|---|
 | 25 | +		Rectangle bounds=visuals.getBounds();  | 
|---|
 | 26 | +		  | 
|---|
 | 27 | +		TimeScale scale=visuals.getTimeScale();  | 
|---|
 | 28 | +		java.util.List<AxisTicMarks> t=AxisTicMarks.allRelevant(scale.getInterval());  | 
|---|
 | 29 | +		  | 
|---|
 | 30 | +		int dateLabelH=model.getDisplay().getInt("timeline.datelabel.height");  | 
|---|
 | 31 | +		int y=bounds.y+bounds.height-dateLabelH;	  | 
|---|
 | 32 | +		  | 
|---|
 | 33 | +		// draw in reverse order so bigger granularity at top.  | 
|---|
 | 34 | +		int n=t.size();  | 
|---|
 | 35 | +		for (int i=0; i<n; i++)  | 
|---|
 | 36 | +		{  | 
|---|
 | 37 | +			render(t.get(i), g, bounds.x, y, dateLabelH-1, bounds.y, i==0, objectLocations);  | 
|---|
 | 38 | +			y-=dateLabelH;  | 
|---|
 | 39 | +		}  | 
|---|
 | 40 | +	}  | 
|---|
 | 41 | +	  | 
|---|
 | 42 | +	void render(AxisTicMarks t, Graphics2D g, int x, int y, int h, int top, boolean full, Collection<Mouseover> objectLocations)  | 
|---|
 | 43 | +	{  | 
|---|
 | 44 | +		TFModel model=visuals.getModel();  | 
|---|
 | 45 | +  | 
|---|
 | 46 | +		int n=t.tics.size();  | 
|---|
 | 47 | +		for (int i=0; i<n-1; i++)  | 
|---|
 | 48 | +		{  | 
|---|
 | 49 | +			  | 
|---|
 | 50 | +			long start=t.tics.get(i);  | 
|---|
 | 51 | +			long end=t.tics.get(i+1);  | 
|---|
 | 52 | +			  | 
|---|
 | 53 | +			int x0=Math.max(x,visuals.getTimeScale().toInt(start));			  | 
|---|
 | 54 | +			int x1=visuals.getTimeScale().toInt(end);  | 
|---|
 | 55 | +			  | 
|---|
 | 56 | +			int dayOfWeek=TimeUtils.cal(start).get(Calendar.DAY_OF_WEEK);  | 
|---|
 | 57 | +			  | 
|---|
 | 58 | +			g.setColor(t.unit.isDayOrLess() && (dayOfWeek==1 || dayOfWeek==7) ?   | 
|---|
 | 59 | +					new Color(245,245,245) : new Color(240,240,240));  | 
|---|
 | 60 | +  | 
|---|
 | 61 | +			g.fillRect(x0, y, x1-x0-1, h);  | 
|---|
 | 62 | +			g.setColor(Color.white);  | 
|---|
 | 63 | +			g.drawLine(x1-1, y, x1-1, y+h);  | 
|---|
 | 64 | +			g.drawLine(x0,y+h,x1,y+h);  | 
|---|
 | 65 | +			objectLocations.add(new Mouseover(new Interval(start,end), x0, y, x1-x0-1, h));  | 
|---|
 | 66 | +			  | 
|---|
 | 67 | +			g.setFont(model.getDisplay().timeLabel());  | 
|---|
 | 68 | +			String label=full? t.unit.formatFull(start) : t.unit.format(new Date(start));  | 
|---|
 | 69 | +			int tx=x0+3;  | 
|---|
 | 70 | +			int ty=y+h-5;  | 
|---|
 | 71 | +			g.setColor(full ? Color.darkGray : Color.gray);  | 
|---|
 | 72 | +			int sw=model.getDisplay().timeLabelFontMetrics().stringWidth(label);  | 
|---|
 | 73 | +			if (sw<x1-tx-3)  | 
|---|
 | 74 | +				g.drawString(label, tx,ty);  | 
|---|
 | 75 | +			else  | 
|---|
 | 76 | +			{  | 
|---|
 | 77 | +				int c=label.indexOf(':');  | 
|---|
 | 78 | +				if (c>0)  | 
|---|
 | 79 | +				{  | 
|---|
 | 80 | +					label=label.substring(0,c);  | 
|---|
 | 81 | +					sw=model.getDisplay().timeLabelFontMetrics().stringWidth(label);  | 
|---|
 | 82 | +					if (sw<x1-tx-3)  | 
|---|
 | 83 | +						g.drawString(label, tx,ty);  | 
|---|
 | 84 | +				}  | 
|---|
 | 85 | +			}  | 
|---|
 | 86 | +		}  | 
|---|
 | 87 | +	}  | 
|---|
 | 88 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.vis.timeline;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import java.util.*;  | 
|---|
 | 4 | +  | 
|---|
 | 5 | +import timeflow.data.time.*;  | 
|---|
 | 6 | +  | 
|---|
 | 7 | +public class AxisTicMarks {  | 
|---|
 | 8 | +	public TimeUnit unit;  | 
|---|
 | 9 | +	public List<Long> tics;  | 
|---|
 | 10 | +	  | 
|---|
 | 11 | +	private static final TimeUnit[] units={  | 
|---|
 | 12 | +		TimeUnit.YEAR, TimeUnit.MONTH, TimeUnit.DAY, TimeUnit.HOUR, TimeUnit.MINUTE, TimeUnit.SECOND  | 
|---|
 | 13 | +	};  | 
|---|
 | 14 | +	  | 
|---|
 | 15 | +	private static final TimeUnit[] histUnits={  | 
|---|
 | 16 | +		TimeUnit.YEAR.times(100), TimeUnit.YEAR.times(50), TimeUnit.YEAR.times(25),   | 
|---|
 | 17 | +			TimeUnit.YEAR.times(10), TimeUnit.YEAR.times(5), TimeUnit.YEAR.times(2), TimeUnit.YEAR,  | 
|---|
 | 18 | +		TimeUnit.MONTH.times(6), TimeUnit.MONTH.times(3), TimeUnit.MONTH.times(2), TimeUnit.MONTH,   | 
|---|
 | 19 | +		TimeUnit.WEEK, TimeUnit.DAY.times(2), TimeUnit.DAY,  | 
|---|
 | 20 | +  | 
|---|
 | 21 | +		TimeUnit.HOUR,   | 
|---|
 | 22 | +		TimeUnit.MINUTE,   | 
|---|
 | 23 | +		TimeUnit.SECOND  | 
|---|
 | 24 | +	};  | 
|---|
 | 25 | +	  | 
|---|
 | 26 | +	public AxisTicMarks(TimeUnit unit, long start, long end)  | 
|---|
 | 27 | +	{  | 
|---|
 | 28 | +		this.unit=unit;  | 
|---|
 | 29 | +		tics=new ArrayList<Long>();  | 
|---|
 | 30 | +		RoughTime r=unit.roundDown(start);  | 
|---|
 | 31 | +		tics.add(r.getTime());  | 
|---|
 | 32 | +		do  | 
|---|
 | 33 | +		{  | 
|---|
 | 34 | +			unit.addTo(r);  | 
|---|
 | 35 | +			tics.add(r.getTime());  | 
|---|
 | 36 | +		} while (r.getTime()<end);	  | 
|---|
 | 37 | +	}  | 
|---|
 | 38 | +	  | 
|---|
 | 39 | +	  | 
|---|
 | 40 | +	  | 
|---|
 | 41 | +	public static List<AxisTicMarks> allRelevant(Interval interval)  | 
|---|
 | 42 | +	{  | 
|---|
 | 43 | +		return allRelevant(interval.start, interval.end);  | 
|---|
 | 44 | +	}  | 
|---|
 | 45 | +	  | 
|---|
 | 46 | +	public static List<AxisTicMarks> allRelevant(long start, long end)  | 
|---|
 | 47 | +	{  | 
|---|
 | 48 | +		return allRelevant(start, end, 40);  | 
|---|
 | 49 | +	}  | 
|---|
 | 50 | +	  | 
|---|
 | 51 | +	public static AxisTicMarks histoTics(long start, long end)  | 
|---|
 | 52 | +	{  | 
|---|
 | 53 | +		for (int i=histUnits.length-1; i>=0; i--)  | 
|---|
 | 54 | +		{  | 
|---|
 | 55 | +			TimeUnit u=histUnits[i];  | 
|---|
 | 56 | +			long estimate=u.approxNumInRange(start, end);		  | 
|---|
 | 57 | +			if (estimate<200 || i==0)  | 
|---|
 | 58 | +			{  | 
|---|
 | 59 | +				AxisTicMarks t=new AxisTicMarks(u, start, end);  | 
|---|
 | 60 | +				return t;  | 
|---|
 | 61 | +			}  | 
|---|
 | 62 | +		}  | 
|---|
 | 63 | +		return null;  | 
|---|
 | 64 | +	}  | 
|---|
 | 65 | +	  | 
|---|
 | 66 | +	public static List<AxisTicMarks> allRelevant(long start, long end, long maxTics)  | 
|---|
 | 67 | +	{  | 
|---|
 | 68 | +		List<AxisTicMarks> list=new ArrayList<AxisTicMarks>();  | 
|---|
 | 69 | +		  | 
|---|
 | 70 | +		  | 
|---|
 | 71 | +		for (int i=0; i<units.length; i++)  | 
|---|
 | 72 | +		{  | 
|---|
 | 73 | +			TimeUnit u=units[i];  | 
|---|
 | 74 | +			long estimate=u.approxNumInRange(start, end);  | 
|---|
 | 75 | +			  | 
|---|
 | 76 | +			if (estimate<maxTics)  | 
|---|
 | 77 | +			{  | 
|---|
 | 78 | +				AxisTicMarks t=new AxisTicMarks(u, start, end);  | 
|---|
 | 79 | +				if (list.size()>0)  | 
|---|
 | 80 | +				{  | 
|---|
 | 81 | +					AxisTicMarks last=list.get(0);  | 
|---|
 | 82 | +					if (last.tics.size()==t.tics.size())  | 
|---|
 | 83 | +						list.remove(0);  | 
|---|
 | 84 | +				}  | 
|---|
 | 85 | +				list.add(t);  | 
|---|
 | 86 | +				  | 
|---|
 | 87 | +			}  | 
|---|
 | 88 | +		}  | 
|---|
 | 89 | +		while (list.size()>2)  | 
|---|
 | 90 | +			list.remove(0);  | 
|---|
 | 91 | +		  | 
|---|
 | 92 | +		if (list.size()==0) // uh oh! must be many years. we will add in bigger increments.  | 
|---|
 | 93 | +		{  | 
|---|
 | 94 | +			long length=end-start;  | 
|---|
 | 95 | +			long size=365*24*60*60*1000L;  | 
|---|
 | 96 | +			int m=1;  | 
|---|
 | 97 | +			maxTics=15;  | 
|---|
 | 98 | +			while (m<2000000000 && length/(m*size)>maxTics)  | 
|---|
 | 99 | +			{  | 
|---|
 | 100 | +				if (length/(2*m*size)<=maxTics)  | 
|---|
 | 101 | +				{  | 
|---|
 | 102 | +					m*=2;  | 
|---|
 | 103 | +					break;  | 
|---|
 | 104 | +				}  | 
|---|
 | 105 | +				if (length/(5*m*size)<=maxTics)  | 
|---|
 | 106 | +				{  | 
|---|
 | 107 | +					m*=5;  | 
|---|
 | 108 | +					break;  | 
|---|
 | 109 | +				}  | 
|---|
 | 110 | +				m*=10;  | 
|---|
 | 111 | +			}	  | 
|---|
 | 112 | +			AxisTicMarks t=new AxisTicMarks(TimeUnit.multipleYears(m), start, end);  | 
|---|
 | 113 | +			list.add(t);  | 
|---|
 | 114 | +		}	  | 
|---|
 | 115 | +		return list;  | 
|---|
 | 116 | +	}  | 
|---|
 | 117 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.vis.timeline;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.data.db.*;  | 
|---|
 | 4 | +import timeflow.data.time.*;  | 
|---|
 | 5 | +import timeflow.model.*;  | 
|---|
 | 6 | +import timeflow.vis.Mouseover;  | 
|---|
 | 7 | +import timeflow.vis.MouseoverLabel;  | 
|---|
 | 8 | +import timeflow.vis.VisualAct;  | 
|---|
 | 9 | +  | 
|---|
 | 10 | +import timeflow.util.*;  | 
|---|
 | 11 | +  | 
|---|
 | 12 | +import java.awt.*;  | 
|---|
 | 13 | +import java.awt.geom.AffineTransform;  | 
|---|
 | 14 | +import java.util.*;  | 
|---|
 | 15 | +import java.util.List;  | 
|---|
 | 16 | +  | 
|---|
 | 17 | +public class TimelineRenderer  | 
|---|
 | 18 | +{  | 
|---|
 | 19 | +  | 
|---|
 | 20 | +        private TimelineVisuals visuals;  | 
|---|
 | 21 | +        private int dy;  | 
|---|
 | 22 | +  | 
|---|
 | 23 | +        public TimelineRenderer(TimelineVisuals visuals)  | 
|---|
 | 24 | +        {  | 
|---|
 | 25 | +                this.visuals = visuals;  | 
|---|
 | 26 | +        }  | 
|---|
 | 27 | +  | 
|---|
 | 28 | +        public void setDY(int dy)  | 
|---|
 | 29 | +        {  | 
|---|
 | 30 | +                this.dy = dy;  | 
|---|
 | 31 | +        }  | 
|---|
 | 32 | +  | 
|---|
 | 33 | +        public void render(Graphics2D g, Collection<Mouseover> objectLocations)  | 
|---|
 | 34 | +        {  | 
|---|
 | 35 | +                AffineTransform old = g.getTransform();  | 
|---|
 | 36 | +                g.setTransform(AffineTransform.getTranslateInstance(0, -dy));  | 
|---|
 | 37 | +  | 
|---|
 | 38 | +                g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);  | 
|---|
 | 39 | +                TFModel model = visuals.getModel();  | 
|---|
 | 40 | +                Display display = model.getDisplay();  | 
|---|
 | 41 | +                ActDB db = model.getDB();  | 
|---|
 | 42 | +  | 
|---|
 | 43 | +                if (display.emptyMessage(g, model))  | 
|---|
 | 44 | +                {  | 
|---|
 | 45 | +                        return;  | 
|---|
 | 46 | +                }  | 
|---|
 | 47 | +  | 
|---|
 | 48 | +                // need to check this, because resize events don't (and shouldn't) register with central TFModel.  | 
|---|
 | 49 | +                visuals.layoutIfChanged();  | 
|---|
 | 50 | +  | 
|---|
 | 51 | +                java.util.List<VisualAct> visualActs = visuals.getVisualActs();  | 
|---|
 | 52 | +  | 
|---|
 | 53 | +                if (visualActs == null || visualActs.size() == 0)  | 
|---|
 | 54 | +                {  | 
|---|
 | 55 | +                        g.drawString("No data", 10, 30);  | 
|---|
 | 56 | +                        return;  | 
|---|
 | 57 | +                }  | 
|---|
 | 58 | +  | 
|---|
 | 59 | +                Rectangle bounds = visuals.getBounds();  | 
|---|
 | 60 | +                boolean colorTrackLabels = db.getField(VirtualField.COLOR) == null || db.getField(VirtualField.COLOR).equals(db.getField(VirtualField.TRACK));  | 
|---|
 | 61 | +  | 
|---|
 | 62 | +                // draw tracks, if more than 1.  | 
|---|
 | 63 | +                if (visuals.trackList.size() > 1)  | 
|---|
 | 64 | +                {  | 
|---|
 | 65 | +                        boolean zebra = true;  | 
|---|
 | 66 | +  | 
|---|
 | 67 | +                        for (TimelineTrack t : visuals.trackList)  | 
|---|
 | 68 | +                        {  | 
|---|
 | 69 | +                                if (zebra)  | 
|---|
 | 70 | +                                {  | 
|---|
 | 71 | +                                        g.setColor(display.getColor("timeline.zebra"));  | 
|---|
 | 72 | +                                        g.fillRect(bounds.x, t.y0, bounds.width, t.y1 - t.y0);  | 
|---|
 | 73 | +                                }  | 
|---|
 | 74 | +                                zebra = !zebra;  | 
|---|
 | 75 | +                                g.setColor(display.getColor("timeline.grid"));  | 
|---|
 | 76 | +                                g.drawLine(bounds.x, t.y0, bounds.x + bounds.width, t.y0);  | 
|---|
 | 77 | +                        }  | 
|---|
 | 78 | +                }  | 
|---|
 | 79 | +  | 
|---|
 | 80 | +                Interval screenInterval = visuals.getViewInterval().subinterval(-.5, 1.5);  | 
|---|
 | 81 | +                AxisTicMarks tics = AxisTicMarks.histoTics(screenInterval.start, screenInterval.end);  | 
|---|
 | 82 | +                for (TimelineTrack t : visuals.trackList)  | 
|---|
 | 83 | +                {  | 
|---|
 | 84 | +                        // now... if not in graph mode, just draw items  | 
|---|
 | 85 | +                        if (visuals.getLayoutStyle() != TimelineVisuals.Layout.GRAPH)//max<(t.y1-t.y0)/20)  | 
|---|
 | 86 | +                        {  | 
|---|
 | 87 | +                                for (VisualAct v : t.visualActs)  | 
|---|
 | 88 | +                                {  | 
|---|
 | 89 | +                                        Mouseover o = v.draw(g, null, bounds, display, true, true);  | 
|---|
 | 90 | +                                        if (o != null)  | 
|---|
 | 91 | +                                        {  | 
|---|
 | 92 | +                                                o.y -= dy;  | 
|---|
 | 93 | +                                                objectLocations.add(o);  | 
|---|
 | 94 | +                                        }  | 
|---|
 | 95 | +                                }  | 
|---|
 | 96 | +                                continue;  | 
|---|
 | 97 | +                        }  | 
|---|
 | 98 | +  | 
|---|
 | 99 | +                if (true) continue;  | 
|---|
 | 100 | +                  | 
|---|
 | 101 | +                        // draw bars. to do so, we make a histogram of visible items.  | 
|---|
 | 102 | +                        t.histogram = new DoubleBag<Long>();  | 
|---|
 | 103 | +                        for (VisualAct v : t.visualActs)  | 
|---|
 | 104 | +                        {  | 
|---|
 | 105 | +                                long time = v.getStart().getTime();  | 
|---|
 | 106 | +                                if (screenInterval.contains(time))  | 
|---|
 | 107 | +                                {  | 
|---|
 | 108 | +                                        t.histogram.add(tics.unit.roundDown(v.getStart().getTime()).getTime(), 1);//v.getSize());  | 
|---|
 | 109 | +                                }  | 
|---|
 | 110 | +                        }  | 
|---|
 | 111 | +  | 
|---|
 | 112 | +                        // get max of items.  | 
|---|
 | 113 | +                        double max = t.histogram.getMax();  | 
|---|
 | 114 | +  | 
|---|
 | 115 | +                        // now draw bars on screen.  | 
|---|
 | 116 | +                        Color fg = colorTrackLabels ? model.getDisplay().makeColor(t.label) : Color.gray;  | 
|---|
 | 117 | +                        if (visuals.trackList.size() < 2)  | 
|---|
 | 118 | +                        {  | 
|---|
 | 119 | +                                fg = Color.gray;  | 
|---|
 | 120 | +                        }  | 
|---|
 | 121 | +  | 
|---|
 | 122 | +                        List<Long> keys = t.histogram.unordered();  | 
|---|
 | 123 | +                        Collections.sort(keys);  | 
|---|
 | 124 | +                        for (Long r : keys)  | 
|---|
 | 125 | +                        {  | 
|---|
 | 126 | +                                double num = t.histogram.num(r);  | 
|---|
 | 127 | +                                int x1 = visuals.getTimeScale().toInt(r);  | 
|---|
 | 128 | +                                int x2 = visuals.getTimeScale().toInt(tics.unit.roundUp(r + 1).getTime());  | 
|---|
 | 129 | +                                int barY = t.y1 - (int) (.9 * ((t.y1 - t.y0) * num) / max);  | 
|---|
 | 130 | +                                g.setColor(new Color(230, 230, 230));  | 
|---|
 | 131 | +                                int m = 12;  | 
|---|
 | 132 | +                                g.fillRoundRect(x1 + 3, barY + 3, x2 - x1 - 1, t.y1 - barY, m, m);  | 
|---|
 | 133 | +  | 
|---|
 | 134 | +                                g.setColor(fg);  | 
|---|
 | 135 | +                                g.fillRoundRect(x1, barY, x2 - x1 - 1, t.y1 - barY, m, m);  | 
|---|
 | 136 | +  | 
|---|
 | 137 | +                                MouseoverLabel mouse = new MouseoverLabel("" + Math.round(num), "items", x1, barY, x2 - x1 - 1, t.y1 - barY);  | 
|---|
 | 138 | +                                objectLocations.add(mouse);  | 
|---|
 | 139 | +                        }  | 
|---|
 | 140 | +                }  | 
|---|
 | 141 | +  | 
|---|
 | 142 | +                // finally label the tracks. we do this last so that the labels go on top of the data.  | 
|---|
 | 143 | +  | 
|---|
 | 144 | +                if (visuals.trackList.size() > 1)  | 
|---|
 | 145 | +                {  | 
|---|
 | 146 | +                        boolean zebra = false;  | 
|---|
 | 147 | +                        FontMetrics hugeFm = display.hugeFontMetrics();  | 
|---|
 | 148 | +                        for (TimelineTrack t : visuals.trackList)  | 
|---|
 | 149 | +                        {  | 
|---|
 | 150 | +  | 
|---|
 | 151 | +                                // now label the track.  | 
|---|
 | 152 | +                                //if (t.y1 - t.y0 > 23)  | 
|---|
 | 153 | +                                {  | 
|---|
 | 154 | +                                        Color fg = colorTrackLabels ? model.getDisplay().makeColor(t.label) : Color.darkGray;  | 
|---|
 | 155 | +                                        Color bg = zebra ? display.getColor("timeline.zebra") : Color.white;  | 
|---|
 | 156 | +  | 
|---|
 | 157 | +                                        String label = t.label;  | 
|---|
 | 158 | +                                        if (label.equals(Display.MISC_CODE))  | 
|---|
 | 159 | +                                        {  | 
|---|
 | 160 | +                                                label = display.getMiscLabel();  | 
|---|
 | 161 | +                                        } else if (label.length() == 0)  | 
|---|
 | 162 | +                                        {  | 
|---|
 | 163 | +                                                label = display.getNullLabel();  | 
|---|
 | 164 | +                                        } else  | 
|---|
 | 165 | +                                        {  | 
|---|
 | 166 | +                                                label = display.format(label, 20, false);  | 
|---|
 | 167 | +                                        }  | 
|---|
 | 168 | +  | 
|---|
 | 169 | +                                        // draw background.  | 
|---|
 | 170 | +                                        g.setColor(bg);  | 
|---|
 | 171 | +                                        int sw = hugeFm.stringWidth(label);  | 
|---|
 | 172 | +                                        g.fillRect(0, t.y1 - 20, sw + 8, 19);  | 
|---|
 | 173 | +  | 
|---|
 | 174 | +                                        // draw foreground (actual label)	  | 
|---|
 | 175 | +                                        g.setFont(display.huge());  | 
|---|
 | 176 | +                                        g.setColor(fg);  | 
|---|
 | 177 | +                                        g.drawString(label, 2, t.y1); //  - 5);  | 
|---|
 | 178 | +                                }  | 
|---|
 | 179 | +                                zebra = !zebra;  | 
|---|
 | 180 | +                        }  | 
|---|
 | 181 | +                }  | 
|---|
 | 182 | +                g.setTransform(old);  | 
|---|
 | 183 | +        }  | 
|---|
 | 184 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.vis.timeline;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.data.db.*;  | 
|---|
 | 4 | +import timeflow.data.time.*;  | 
|---|
 | 5 | +import timeflow.model.*;  | 
|---|
 | 6 | +import timeflow.vis.TimeScale;  | 
|---|
 | 7 | +import timeflow.vis.VisualAct;  | 
|---|
 | 8 | +import timeflow.vis.timeline.*;  | 
|---|
 | 9 | +  | 
|---|
 | 10 | +import timeflow.util.*;  | 
|---|
 | 11 | +  | 
|---|
 | 12 | +import java.awt.*;  | 
|---|
 | 13 | +import javax.swing.*;  | 
|---|
 | 14 | +import java.awt.event.*;  | 
|---|
 | 15 | +  | 
|---|
 | 16 | +public class TimelineSlider extends ModelPanel  | 
|---|
 | 17 | +{  | 
|---|
 | 18 | +  | 
|---|
 | 19 | +        TimelineVisuals visuals;  | 
|---|
 | 20 | +        Interval original;  | 
|---|
 | 21 | +        long minRange;  | 
|---|
 | 22 | +        int ew = 10;  | 
|---|
 | 23 | +        int eventRadius = 2;  | 
|---|
 | 24 | +        TimeScale scale;  | 
|---|
 | 25 | +        Point mouseHit = new Point();  | 
|---|
 | 26 | +        Point mouse = new Point(-1, 0);  | 
|---|
 | 27 | +  | 
|---|
 | 28 | +        enum Modify  | 
|---|
 | 29 | +        {  | 
|---|
 | 30 | +  | 
|---|
 | 31 | +                START, END, POSITION, NONE  | 
|---|
 | 32 | +        };  | 
|---|
 | 33 | +        Modify change = Modify.NONE;  | 
|---|
 | 34 | +        Rectangle startRect = new Rectangle(-1, -1, 0, 0);  | 
|---|
 | 35 | +        Rectangle endRect = new Rectangle(-1, -1, 0, 0);  | 
|---|
 | 36 | +        Rectangle positionRect = new Rectangle(-1, -1, 0, 0);  | 
|---|
 | 37 | +        Color sidePlain = Color.orange;  | 
|---|
 | 38 | +        Color sideMouse = new Color(230, 100, 0);  | 
|---|
 | 39 | +  | 
|---|
 | 40 | +        public TimelineSlider(final TimelineVisuals visuals, final long minRange, final Runnable action)  | 
|---|
 | 41 | +        {  | 
|---|
 | 42 | +                super(visuals.getModel());  | 
|---|
 | 43 | +  | 
|---|
 | 44 | +                this.minRange = minRange;  | 
|---|
 | 45 | +                this.visuals = visuals;  | 
|---|
 | 46 | +  | 
|---|
 | 47 | +                addMouseListener(new MouseAdapter()  | 
|---|
 | 48 | +                {  | 
|---|
 | 49 | +  | 
|---|
 | 50 | +                        @Override  | 
|---|
 | 51 | +                        public void mousePressed(MouseEvent e)  | 
|---|
 | 52 | +                        {  | 
|---|
 | 53 | +                                int mx = e.getX();  | 
|---|
 | 54 | +                                int my = e.getY();  | 
|---|
 | 55 | +                                if (positionRect.contains(mx, my))  | 
|---|
 | 56 | +                                {  | 
|---|
 | 57 | +                                        change = Modify.POSITION;  | 
|---|
 | 58 | +                                } else if (startRect.contains(mx, my))  | 
|---|
 | 59 | +                                {  | 
|---|
 | 60 | +                                        change = Modify.START;  | 
|---|
 | 61 | +                                } else if (endRect.contains(mx, my))  | 
|---|
 | 62 | +                                {  | 
|---|
 | 63 | +                                        change = Modify.END;  | 
|---|
 | 64 | +                                } else  | 
|---|
 | 65 | +                                {  | 
|---|
 | 66 | +                                        change = Modify.NONE;  | 
|---|
 | 67 | +                                }  | 
|---|
 | 68 | +                                mouseHit.setLocation(mx, my);  | 
|---|
 | 69 | +                                original = window().copy();  | 
|---|
 | 70 | +                                mouse.setLocation(mx, my);  | 
|---|
 | 71 | +                                repaint();  | 
|---|
 | 72 | +                        }  | 
|---|
 | 73 | +  | 
|---|
 | 74 | +                        @Override  | 
|---|
 | 75 | +                        public void mouseReleased(MouseEvent e)  | 
|---|
 | 76 | +                        {  | 
|---|
 | 77 | +                                change = Modify.NONE;  | 
|---|
 | 78 | +                                repaint();  | 
|---|
 | 79 | +                        }  | 
|---|
 | 80 | +                });  | 
|---|
 | 81 | +                addMouseMotionListener(new MouseMotionAdapter()  | 
|---|
 | 82 | +                {  | 
|---|
 | 83 | +  | 
|---|
 | 84 | +                        @Override  | 
|---|
 | 85 | +                        public void mouseDragged(MouseEvent e)  | 
|---|
 | 86 | +                        {  | 
|---|
 | 87 | +  | 
|---|
 | 88 | +                                if (change == Modify.NONE)  | 
|---|
 | 89 | +                                {  | 
|---|
 | 90 | +                                        return;  | 
|---|
 | 91 | +                                }  | 
|---|
 | 92 | +                                mouse.setLocation(e.getX(), e.getY());  | 
|---|
 | 93 | +                                int mouseDiff = mouse.x - mouseHit.x;  | 
|---|
 | 94 | +                                Interval limits = visuals.getGlobalInterval();  | 
|---|
 | 95 | +                                long timeDiff = scale.spaceToTime(mouseDiff);  | 
|---|
 | 96 | +  | 
|---|
 | 97 | +                                switch (change)  | 
|---|
 | 98 | +                                {  | 
|---|
 | 99 | +                                        case POSITION:  | 
|---|
 | 100 | +                                                window().translateTo(original.start + timeDiff);  | 
|---|
 | 101 | +                                                window().clampInside(limits);  | 
|---|
 | 102 | +                                                break;  | 
|---|
 | 103 | +                                        case START:  | 
|---|
 | 104 | +                                                window().start = Math.min(original.start + timeDiff, original.end - minRange);  | 
|---|
 | 105 | +                                                window().start = Math.max(window().start, limits.start);  | 
|---|
 | 106 | +                                                break;  | 
|---|
 | 107 | +                                        case END:  | 
|---|
 | 108 | +                                                window().end = Math.max(original.end + timeDiff, original.start + minRange);  | 
|---|
 | 109 | +                                                window().end = Math.min(window().end, limits.end);  | 
|---|
 | 110 | +                                }  | 
|---|
 | 111 | +                                getModel().setViewInterval(window());  | 
|---|
 | 112 | +                                action.run();  | 
|---|
 | 113 | +                                repaint();  | 
|---|
 | 114 | +                        }  | 
|---|
 | 115 | +                });  | 
|---|
 | 116 | +        }  | 
|---|
 | 117 | +  | 
|---|
 | 118 | +        private Interval window()  | 
|---|
 | 119 | +        {  | 
|---|
 | 120 | +                return visuals.getViewInterval();  | 
|---|
 | 121 | +        }  | 
|---|
 | 122 | +  | 
|---|
 | 123 | +        @Override  | 
|---|
 | 124 | +        public Dimension getPreferredSize()  | 
|---|
 | 125 | +        {  | 
|---|
 | 126 | +                return new Dimension(600, 30);  | 
|---|
 | 127 | +        }  | 
|---|
 | 128 | +  | 
|---|
 | 129 | +        public void setMinRange(long minRange)  | 
|---|
 | 130 | +        {  | 
|---|
 | 131 | +                this.minRange = minRange;  | 
|---|
 | 132 | +        }  | 
|---|
 | 133 | +  | 
|---|
 | 134 | +        @Override  | 
|---|
 | 135 | +        public void note(TFEvent e)  | 
|---|
 | 136 | +        {  | 
|---|
 | 137 | +                repaint();  | 
|---|
 | 138 | +        }  | 
|---|
 | 139 | +  | 
|---|
 | 140 | +        void setTimeInterval(Interval interval)  | 
|---|
 | 141 | +        {  | 
|---|
 | 142 | +                window().setTo(interval);  | 
|---|
 | 143 | +                repaint();  | 
|---|
 | 144 | +        }  | 
|---|
 | 145 | +  | 
|---|
 | 146 | +        public void paintComponent(Graphics g1)  | 
|---|
 | 147 | +        {  | 
|---|
 | 148 | +                int w = getSize().width, h = getSize().height;  | 
|---|
 | 149 | +                Graphics2D g = (Graphics2D) g1;  | 
|---|
 | 150 | +  | 
|---|
 | 151 | +                long start = System.currentTimeMillis();  | 
|---|
 | 152 | +  | 
|---|
 | 153 | +                // draw main backdrop.  | 
|---|
 | 154 | +                g.setColor(Color.white);  | 
|---|
 | 155 | +                g.fillRect(0, 0, w, h);  | 
|---|
 | 156 | +  | 
|---|
 | 157 | +                if (visuals.getModel() == null || visuals.getModel().getActs() == null)  | 
|---|
 | 158 | +                {  | 
|---|
 | 159 | +                        g.setColor(Color.darkGray);  | 
|---|
 | 160 | +                        g.drawString("No data for timeline.", 5, 20);  | 
|---|
 | 161 | +                        return;  | 
|---|
 | 162 | +                }  | 
|---|
 | 163 | +  | 
|---|
 | 164 | +                scale = new TimeScale();  | 
|---|
 | 165 | +                scale.setDateRange(visuals.getGlobalInterval());  | 
|---|
 | 166 | +                scale.setNumberRange(ew, w - ew);  | 
|---|
 | 167 | +  | 
|---|
 | 168 | +  | 
|---|
 | 169 | +                // draw the area for the central "thumb".  | 
|---|
 | 170 | +                int lx = scale.toInt(window().start);  | 
|---|
 | 171 | +                int rx = scale.toInt(window().end);  | 
|---|
 | 172 | +                g.setColor(change == Modify.POSITION ? new Color(255, 255, 120) : new Color(255, 245, 200));  | 
|---|
 | 173 | +                positionRect.setBounds(lx, 0, rx - lx, h);  | 
|---|
 | 174 | +                g.fill(positionRect);  | 
|---|
 | 175 | +  | 
|---|
 | 176 | +                // Figure out how best to draw events.  | 
|---|
 | 177 | +                // If there are too many, we just draw a kind of histogram of frequency,  | 
|---|
 | 178 | +                // rather than using the timeline layout.  | 
|---|
 | 179 | +                int slotW = 2 * eventRadius;  | 
|---|
 | 180 | +                int slotNum = w / slotW + 1;  | 
|---|
 | 181 | +                int[] slots = new int[slotNum];  | 
|---|
 | 182 | +                int mostInSlot = 0;  | 
|---|
 | 183 | +                for (VisualAct v : visuals.getVisualActs())  | 
|---|
 | 184 | +                {  | 
|---|
 | 185 | +                        if (!v.isVisible())  | 
|---|
 | 186 | +                        {  | 
|---|
 | 187 | +                                continue;  | 
|---|
 | 188 | +                        }  | 
|---|
 | 189 | +                        int x = scale.toInt(v.getStart().getTime());  | 
|---|
 | 190 | +                        int s = x / slotW;  | 
|---|
 | 191 | +                        if (s >= 0 && s < slotNum)  | 
|---|
 | 192 | +                        {  | 
|---|
 | 193 | +                                slots[s]++;  | 
|---|
 | 194 | +                                mostInSlot = Math.max(mostInSlot, slots[s]);  | 
|---|
 | 195 | +                        }  | 
|---|
 | 196 | +                }  | 
|---|
 | 197 | +                if (mostInSlot > 30)  | 
|---|
 | 198 | +                {  | 
|---|
 | 199 | +                        g.setColor(Color.gray);  | 
|---|
 | 200 | +                        for (int i = 0; i < slots.length; i++)  | 
|---|
 | 201 | +                        {  | 
|---|
 | 202 | +                                int sh = (h * slots[i]) / mostInSlot;  | 
|---|
 | 203 | +                                g.fillRect(slotW * i, h - sh, slotW, sh);  | 
|---|
 | 204 | +                        }  | 
|---|
 | 205 | +                } else  | 
|---|
 | 206 | +                {  | 
|---|
 | 207 | +                        // draw individual events.  | 
|---|
 | 208 | +                        for (VisualAct v : visuals.getVisualActs())  | 
|---|
 | 209 | +                        {  | 
|---|
 | 210 | +                                if (!v.isVisible())  | 
|---|
 | 211 | +                                {  | 
|---|
 | 212 | +                                        continue;  | 
|---|
 | 213 | +                                }  | 
|---|
 | 214 | +                                g.setColor(v.getColor());  | 
|---|
 | 215 | +                                int x = scale.toInt(v.getStart().getTime());  | 
|---|
 | 216 | +  | 
|---|
 | 217 | +                                int y = eventRadius + (int) (v.getY() * h) / (visuals.getBounds().height - 2 * eventRadius);  | 
|---|
 | 218 | +                                g.fillRect(x - 1, y - eventRadius, 2 * eventRadius, 3);  | 
|---|
 | 219 | +                                if (v.getEnd() != null)  | 
|---|
 | 220 | +                                {  | 
|---|
 | 221 | +                                        int endX = scale.toInt(v.getEnd().getTime());  | 
|---|
 | 222 | +                                        g.drawLine(x, y, endX, y);  | 
|---|
 | 223 | +                                }  | 
|---|
 | 224 | +                        }  | 
|---|
 | 225 | +                }  | 
|---|
 | 226 | +  | 
|---|
 | 227 | +                g.setColor(Color.gray);  | 
|---|
 | 228 | +                g.drawLine(0, 0, w, 0);  | 
|---|
 | 229 | +                g.drawLine(0, h - 1, w, h - 1);  | 
|---|
 | 230 | +  | 
|---|
 | 231 | +                // draw "expansion" areas on sides of thumb.  | 
|---|
 | 232 | +                startRect.setBounds(positionRect.x - ew, 1, ew, h - 2);  | 
|---|
 | 233 | +                g.setColor(change == Modify.START ? sideMouse : sidePlain);  | 
|---|
 | 234 | +                g.fill(startRect);  | 
|---|
 | 235 | +                endRect.setBounds(positionRect.x + positionRect.width, 1, ew, h - 2);  | 
|---|
 | 236 | +                g.setColor(change == Modify.END ? sideMouse : sidePlain);  | 
|---|
 | 237 | +                g.fill(endRect);  | 
|---|
 | 238 | +        }  | 
|---|
 | 239 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.vis.timeline;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.vis.VisualAct;  | 
|---|
 | 4 | +import timeflow.data.time.*;  | 
|---|
 | 5 | +import timeflow.util.*;  | 
|---|
 | 6 | +  | 
|---|
 | 7 | +import java.util.*;  | 
|---|
 | 8 | +  | 
|---|
 | 9 | +public class TimelineTrack implements Comparable  | 
|---|
 | 10 | +{  | 
|---|
 | 11 | +  | 
|---|
 | 12 | +        String label;  | 
|---|
 | 13 | +        List<VisualAct> visualActs = new ArrayList<VisualAct>();  | 
|---|
 | 14 | +        int y0, y1;  | 
|---|
 | 15 | +        DoubleBag<Long> histogram;  | 
|---|
 | 16 | +  | 
|---|
 | 17 | +        TimelineTrack(String label)  | 
|---|
 | 18 | +        {  | 
|---|
 | 19 | +                this.label = label;  | 
|---|
 | 20 | +        }  | 
|---|
 | 21 | +  | 
|---|
 | 22 | +        void add(VisualAct v)  | 
|---|
 | 23 | +        {  | 
|---|
 | 24 | +                visualActs.add(v);  | 
|---|
 | 25 | +        }  | 
|---|
 | 26 | +  | 
|---|
 | 27 | +        int size()  | 
|---|
 | 28 | +        {  | 
|---|
 | 29 | +                return visualActs.size();  | 
|---|
 | 30 | +        }  | 
|---|
 | 31 | +  | 
|---|
 | 32 | +        // assumes a>b>0  | 
|---|
 | 33 | +        int gcd(int a, int b)  | 
|---|
 | 34 | +        {  | 
|---|
 | 35 | +                int mod = a % b;  | 
|---|
 | 36 | +                if (mod == 0)  | 
|---|
 | 37 | +                {  | 
|---|
 | 38 | +                        return b;  | 
|---|
 | 39 | +                }  | 
|---|
 | 40 | +                return gcd(b, mod);  | 
|---|
 | 41 | +        }  | 
|---|
 | 42 | +  | 
|---|
 | 43 | +        int nearAndRelPrime(int target, int modulus)  | 
|---|
 | 44 | +        {  | 
|---|
 | 45 | +                if (target < 2)  | 
|---|
 | 46 | +                {  | 
|---|
 | 47 | +                        return 1;  | 
|---|
 | 48 | +                }  | 
|---|
 | 49 | +                while (gcd(modulus, target) > 1)  | 
|---|
 | 50 | +                {  | 
|---|
 | 51 | +                        target--;  | 
|---|
 | 52 | +                }  | 
|---|
 | 53 | +                return target;  | 
|---|
 | 54 | +        }  | 
|---|
 | 55 | +  | 
|---|
 | 56 | +        // top and height are in proportion of total height of frame.  | 
|---|
 | 57 | +        void layout(double top, double height, TimelineVisuals visuals)  | 
|---|
 | 58 | +        {  | 
|---|
 | 59 | +                int n = visualActs.size();  | 
|---|
 | 60 | +                if (n == 0)  | 
|---|
 | 61 | +                {  | 
|---|
 | 62 | +                        return;  | 
|---|
 | 63 | +                }  | 
|---|
 | 64 | +                  | 
|---|
 | 65 | +                int labelHeight = 0; // 80;  | 
|---|
 | 66 | +                int fh = visuals.getBounds().height - labelHeight;  | 
|---|
 | 67 | +                int fy = visuals.getBounds().y;  | 
|---|
 | 68 | +                int cellH = visuals.getModel().getDisplay().getInt("timeline.item.height.min");  | 
|---|
 | 69 | +  | 
|---|
 | 70 | +                int fontHeight = 12;  | 
|---|
 | 71 | +                  | 
|---|
 | 72 | +                y0 = fy + (int) (fh * top);  | 
|---|
 | 73 | +                y1 = fy + (int) (fh * (top + height));  | 
|---|
 | 74 | +                //int mid = (y0 + y1) / 2;  | 
|---|
 | 75 | +                int iy0 = y0; // Math.min(y0 + 5, mid);  | 
|---|
 | 76 | +                int iy1 = y1; // Math.max(y1 - 15, mid);  | 
|---|
 | 77 | +  | 
|---|
 | 78 | +                int numCells = Math.max(1, (iy1 - iy0) / cellH);  | 
|---|
 | 79 | +  | 
|---|
 | 80 | +                VisualAct[] rights = new VisualAct[numCells];  | 
|---|
 | 81 | +  | 
|---|
 | 82 | +                int step = nearAndRelPrime((int) (.61803399 * numCells), numCells);  | 
|---|
 | 83 | +                int i = 0;  | 
|---|
 | 84 | +                VisualAct last = null;  | 
|---|
 | 85 | +                for (VisualAct v : visualActs)  | 
|---|
 | 86 | +                {  | 
|---|
 | 87 | +                        if (!v.isVisible() || !v.getTrack().equals(this))  | 
|---|
 | 88 | +                        {  | 
|---|
 | 89 | +                                continue;  | 
|---|
 | 90 | +                        }  | 
|---|
 | 91 | +                        v.setSpaceToRight(1000);  | 
|---|
 | 92 | +  | 
|---|
 | 93 | +                        double num = visuals.getTimeScale().toNum(v.getStart().getTime());  | 
|---|
 | 94 | +                        int x = (int) num;  | 
|---|
 | 95 | +  | 
|---|
 | 96 | +                        int cell = numCells < 2 ? 0 : (i % numCells);  | 
|---|
 | 97 | +                        int y = iy0 + cell * cellH; // (iy1 - iy0 < 12 ? 0 : cell * cellH);  | 
|---|
 | 98 | +                        v.setX(x);  | 
|---|
 | 99 | +                        v.setY(y + fontHeight);  | 
|---|
 | 100 | +  | 
|---|
 | 101 | +                        if (v.getEnd() != null)  | 
|---|
 | 102 | +                        {  | 
|---|
 | 103 | +                                v.setEndX((int) visuals.getTimeScale().toNum(v.getEnd().getTime()));  | 
|---|
 | 104 | +                        }  | 
|---|
 | 105 | +  | 
|---|
 | 106 | +                        if (rights[cell] != null)  | 
|---|
 | 107 | +                        {  | 
|---|
 | 108 | +                                int space = x - rights[cell].getX();  | 
|---|
 | 109 | +                                rights[cell].setSpaceToRight(space);  | 
|---|
 | 110 | +                        }  | 
|---|
 | 111 | +                        rights[cell] = v;  | 
|---|
 | 112 | +                        if ((last != null && v.getStart().getTime() == last.getStart().getTime())  | 
|---|
 | 113 | +                                || visuals.getLayoutStyle() == TimelineVisuals.Layout.TIGHT)  | 
|---|
 | 114 | +                        {  | 
|---|
 | 115 | +                                i++;  | 
|---|
 | 116 | +                        } else  | 
|---|
 | 117 | +                        {  | 
|---|
 | 118 | +                                i += step;  | 
|---|
 | 119 | +                        }  | 
|---|
 | 120 | +                        last = v;  | 
|---|
 | 121 | +                }  | 
|---|
 | 122 | +        }  | 
|---|
 | 123 | +  | 
|---|
 | 124 | +        @Override  | 
|---|
 | 125 | +        public int compareTo(Object o)  | 
|---|
 | 126 | +        {  | 
|---|
 | 127 | +                return ((TimelineTrack) o).size() - size();  | 
|---|
 | 128 | +        }  | 
|---|
 | 129 | +}  | 
|---|
 
| .. | .. | 
|---|
 | 1 | +package timeflow.vis.timeline;  | 
|---|
 | 2 | +  | 
|---|
 | 3 | +import timeflow.data.db.*;  | 
|---|
 | 4 | +import timeflow.data.db.filter.*;  | 
|---|
 | 5 | +import timeflow.data.time.*;  | 
|---|
 | 6 | +import timeflow.model.*;  | 
|---|
 | 7 | +import timeflow.vis.*;  | 
|---|
 | 8 | +  | 
|---|
 | 9 | +import java.util.*;  | 
|---|
 | 10 | +import java.awt.*;  | 
|---|
 | 11 | +  | 
|---|
 | 12 | +/*  | 
|---|
 | 13 | + * A VisualEncoding takes the info about which fields to translate to  | 
|---|
 | 14 | + * which visual aspects, and applies that to particular Acts.  | 
|---|
 | 15 | + */  | 
|---|
 | 16 | +public class TimelineVisuals  | 
|---|
 | 17 | +{  | 
|---|
 | 18 | +  | 
|---|
 | 19 | +        private Map<String, TimelineTrack> trackTable = new HashMap<String, TimelineTrack>();  | 
|---|
 | 20 | +        ArrayList<TimelineTrack> trackList = new ArrayList<TimelineTrack>();  | 
|---|
 | 21 | +        private TimeScale timeScale = new TimeScale();  | 
|---|
 | 22 | +        private Rectangle bounds = new Rectangle();  | 
|---|
 | 23 | +        private boolean frameChanged;  | 
|---|
 | 24 | +        private int numShown = 0;  | 
|---|
 | 25 | +        private Interval globalInterval;  | 
|---|
 | 26 | +  | 
|---|
 | 27 | +        public double scale = 1;  | 
|---|
 | 28 | +          | 
|---|
 | 29 | +        public enum Layout  | 
|---|
 | 30 | +        {  | 
|---|
 | 31 | +  | 
|---|
 | 32 | +                TIGHT, LOOSE, GRAPH  | 
|---|
 | 33 | +        };  | 
|---|
 | 34 | +        private Layout layoutStyle = Layout.TIGHT;  | 
|---|
 | 35 | +        private VisualEncoder encoder;  | 
|---|
 | 36 | +        private TFModel model;  | 
|---|
 | 37 | +        private int fullHeight;  | 
|---|
 | 38 | +  | 
|---|
 | 39 | +        public int getFullHeight()  | 
|---|
 | 40 | +        {  | 
|---|
 | 41 | +                return fullHeight;  | 
|---|
 | 42 | +        }  | 
|---|
 | 43 | +  | 
|---|
 | 44 | +        public TimelineVisuals(TFModel model)  | 
|---|
 | 45 | +        {  | 
|---|
 | 46 | +                this.model = model;  | 
|---|
 | 47 | +                encoder = new VisualEncoder(model);  | 
|---|
 | 48 | +        }  | 
|---|
 | 49 | +  | 
|---|
 | 50 | +        public TimeScale getTimeScale()  | 
|---|
 | 51 | +        {  | 
|---|
 | 52 | +                return timeScale;  | 
|---|
 | 53 | +        }  | 
|---|
 | 54 | +  | 
|---|
 | 55 | +        public Rectangle getBounds()  | 
|---|
 | 56 | +        {  | 
|---|
 | 57 | +                return bounds;  | 
|---|
 | 58 | +        }  | 
|---|
 | 59 | +  | 
|---|
 | 60 | +        public void setBounds(int x, int y, int w, int h)  | 
|---|
 | 61 | +        {  | 
|---|
 | 62 | +                bounds.setBounds(x, y, w, h);  | 
|---|
 | 63 | +                timeScale.setLow(x);  | 
|---|
 | 64 | +                timeScale.setHigh(x + w);  | 
|---|
 | 65 | +                frameChanged = true;  | 
|---|
 | 66 | +        }  | 
|---|
 | 67 | +  | 
|---|
 | 68 | +        public Layout getLayoutStyle()  | 
|---|
 | 69 | +        {  | 
|---|
 | 70 | +                return layoutStyle;  | 
|---|
 | 71 | +        }  | 
|---|
 | 72 | +  | 
|---|
 | 73 | +        public void setLayoutStyle(Layout style)  | 
|---|
 | 74 | +        {  | 
|---|
 | 75 | +                layoutStyle = style;  | 
|---|
 | 76 | +                layout();  | 
|---|
 | 77 | +        }  | 
|---|
 | 78 | +  | 
|---|
 | 79 | +        public Interval getFitToVisibleRange()  | 
|---|
 | 80 | +        {  | 
|---|
 | 81 | +                ActList acts = model.getActs();  | 
|---|
 | 82 | +  | 
|---|
 | 83 | +                // add a little bit to the right so we can see labels...  | 
|---|
 | 84 | +                ActDB db = getModel().getDB();  | 
|---|
 | 85 | +                Field endField = db.getField(VirtualField.END);  | 
|---|
 | 86 | +                Interval i = null;  | 
|---|
 | 87 | +                if (endField == null)  | 
|---|
 | 88 | +                {  | 
|---|
 | 89 | +                        i = DBUtils.range(acts, VirtualField.START);  | 
|---|
 | 90 | +                } else  | 
|---|
 | 91 | +                {  | 
|---|
 | 92 | +                        i = DBUtils.range(acts, new Field[]  | 
|---|
 | 93 | +                                {  | 
|---|
 | 94 | +                                        db.getField(VirtualField.START), endField  | 
|---|
 | 95 | +                                });  | 
|---|
 | 96 | +                }  | 
|---|
 | 97 | +                if (i.length() == 0)  | 
|---|
 | 98 | +                {  | 
|---|
 | 99 | +                        i.expand(globalInterval.length() / 20);  | 
|---|
 | 100 | +                }  | 
|---|
 | 101 | +                i = i.subinterval(-.05, 1.1);  | 
|---|
 | 102 | +                i.intersection(globalInterval);  | 
|---|
 | 103 | +                return i;  | 
|---|
 | 104 | +        }  | 
|---|
 | 105 | +  | 
|---|
 | 106 | +        public void fitToVisible()  | 
|---|
 | 107 | +        {  | 
|---|
 | 108 | +                Interval i = getFitToVisibleRange();  | 
|---|
 | 109 | +                setTimeBounds(i.start, i.end);  | 
|---|
 | 110 | +        }  | 
|---|
 | 111 | +  | 
|---|
 | 112 | +        public void zoomOut()  | 
|---|
 | 113 | +        {  | 
|---|
 | 114 | +                setTimeBounds(globalInterval.start, globalInterval.end);  | 
|---|
 | 115 | +        }  | 
|---|
 | 116 | +  | 
|---|
 | 117 | +        public void setTimeBounds(long first, long last)  | 
|---|
 | 118 | +        {  | 
|---|
 | 119 | +                timeScale.setDateRange(first, last);  | 
|---|
 | 120 | +                frameChanged = true;  | 
|---|
 | 121 | +                model.setViewInterval(new Interval(first, last));  | 
|---|
 | 122 | +        }  | 
|---|
 | 123 | +  | 
|---|
 | 124 | +        public Interval getGlobalInterval()  | 
|---|
 | 125 | +        {  | 
|---|
 | 126 | +                if (globalInterval == null && model != null && model.getDB() != null)  | 
|---|
 | 127 | +                {  | 
|---|
 | 128 | +                        createGlobalInterval();  | 
|---|
 | 129 | +                }  | 
|---|
 | 130 | +                return globalInterval;  | 
|---|
 | 131 | +        }  | 
|---|
 | 132 | +  | 
|---|
 | 133 | +        public void createGlobalInterval()  | 
|---|
 | 134 | +        {  | 
|---|
 | 135 | +                globalInterval = DBUtils.range(model.getDB().all(), VirtualField.START).subinterval(-.05, 1.1);  | 
|---|
 | 136 | +        }  | 
|---|
 | 137 | +  | 
|---|
 | 138 | +        public Interval getViewInterval()  | 
|---|
 | 139 | +        {  | 
|---|
 | 140 | +                return timeScale.getInterval();  | 
|---|
 | 141 | +        }  | 
|---|
 | 142 | +  | 
|---|
 | 143 | +        public java.util.List<VisualAct> getVisualActs()  | 
|---|
 | 144 | +        {  | 
|---|
 | 145 | +                return encoder.getVisualActs();  | 
|---|
 | 146 | +        }  | 
|---|
 | 147 | +  | 
|---|
 | 148 | +        public void layoutIfChanged()  | 
|---|
 | 149 | +        {  | 
|---|
 | 150 | +                if (frameChanged)  | 
|---|
 | 151 | +                {  | 
|---|
 | 152 | +                        layout();  | 
|---|
 | 153 | +                }  | 
|---|
 | 154 | +        }  | 
|---|
 | 155 | +  | 
|---|
 | 156 | +        public void init(boolean majorChange)  | 
|---|
 | 157 | +        {  | 
|---|
 | 158 | +                note(new TFEvent(majorChange ? TFEvent.Type.DATABASE_CHANGE : TFEvent.Type.ACT_CHANGE, null));  | 
|---|
 | 159 | +        }  | 
|---|
 | 160 | +  | 
|---|
 | 161 | +        public void note(TFEvent e)  | 
|---|
 | 162 | +        {  | 
|---|
 | 163 | +                ActList all = null;  | 
|---|
 | 164 | +                if (e.type == TFEvent.Type.DATABASE_CHANGE)  | 
|---|
 | 165 | +                {  | 
|---|
 | 166 | +                        all = model.getDB().all();  | 
|---|
 | 167 | +                        createGlobalInterval();  | 
|---|
 | 168 | +                        Interval i = guessInitialViewInterval(all, globalInterval);  | 
|---|
 | 169 | +                        setTimeBounds(i.start, i.end);  | 
|---|
 | 170 | +                }  | 
|---|
 | 171 | +                if (e.affectsRowSet())  | 
|---|
 | 172 | +                {  | 
|---|
 | 173 | +                        all = model.getDB().all();  | 
|---|
 | 174 | +                        encoder.createVisualActs();  | 
|---|
 | 175 | +                        createGlobalInterval();  | 
|---|
 | 176 | +                } else  | 
|---|
 | 177 | +                {  | 
|---|
 | 178 | +                        encoder.createVisualActs();  | 
|---|
 | 179 | +                }  | 
|---|
 | 180 | +                Interval v = model.getViewInterval();  | 
|---|
 | 181 | +                if (v != null && v.start != timeScale.getInterval().start)  | 
|---|
 | 182 | +                {  | 
|---|
 | 183 | +                        timeScale.getInterval().translateTo(v.start);  | 
|---|
 | 184 | +                }  | 
|---|
 | 185 | +                updateVisuals();  | 
|---|
 | 186 | +        }  | 
|---|
 | 187 | +  | 
|---|
 | 188 | +        private Interval guessInitialViewInterval(ActList acts, Interval fullRange)  | 
|---|
 | 189 | +        {  | 
|---|
 | 190 | +                if (acts.size() < 50)  | 
|---|
 | 191 | +                {  | 
|---|
 | 192 | +                        return fullRange.copy();  | 
|---|
 | 193 | +                }  | 
|---|
 | 194 | +  | 
|---|
 | 195 | +                Interval best = null;  | 
|---|
 | 196 | +                int most = -1;  | 
|---|
 | 197 | +                double d = Math.max(.1, 50. / acts.size());  | 
|---|
 | 198 | +                d = Math.min(1. / 3, d);  | 
|---|
 | 199 | +                for (double x = 0; x < 1 - d; x += d / 4)  | 
|---|
 | 200 | +                {  | 
|---|
 | 201 | +                        Interval i = fullRange.subinterval(x, x + d);  | 
|---|
 | 202 | +                        TimeIntervalFilter f = new TimeIntervalFilter(i, getModel().getDB().getField(VirtualField.START));  | 
|---|
 | 203 | +                        int num = 0;  | 
|---|
 | 204 | +                        for (Act a : acts)  | 
|---|
 | 205 | +                        {  | 
|---|
 | 206 | +                                if (f.accept(a))  | 
|---|
 | 207 | +                                {  | 
|---|
 | 208 | +                                        num++;  | 
|---|
 | 209 | +                                }  | 
|---|
 | 210 | +                        }  | 
|---|
 | 211 | +                        if (num > most)  | 
|---|
 | 212 | +                        {  | 
|---|
 | 213 | +                                most = num;  | 
|---|
 | 214 | +                                best = i;  | 
|---|
 | 215 | +                        }  | 
|---|
 | 216 | +                }  | 
|---|
 | 217 | +                return best;  | 
|---|
 | 218 | +        }  | 
|---|
 | 219 | +  | 
|---|
 | 220 | +        public void updateVisuals()  | 
|---|
 | 221 | +        {  | 
|---|
 | 222 | +                scale = 1;  | 
|---|
 | 223 | +                updateVisualEncoding();  | 
|---|
 | 224 | +                layout();  | 
|---|
 | 225 | +        }  | 
|---|
 | 226 | +  | 
|---|
 | 227 | +        public TFModel getModel()  | 
|---|
 | 228 | +        {  | 
|---|
 | 229 | +                return model;  | 
|---|
 | 230 | +        }  | 
|---|
 | 231 | +  | 
|---|
 | 232 | +        public int getNumTracks()  | 
|---|
 | 233 | +        {  | 
|---|
 | 234 | +                return trackList.size();  | 
|---|
 | 235 | +        }  | 
|---|
 | 236 | +  | 
|---|
 | 237 | +        public double layout()  | 
|---|
 | 238 | +        {  | 
|---|
 | 239 | +                ActList acts = model.getActs();  | 
|---|
 | 240 | +                if (acts == null)  | 
|---|
 | 241 | +                {  | 
|---|
 | 242 | +                        return scale;  | 
|---|
 | 243 | +                }  | 
|---|
 | 244 | +  | 
|---|
 | 245 | +                double labelHeight = 30;  | 
|---|
 | 246 | +                  | 
|---|
 | 247 | +                double min = bounds.height == 0 ? 0 : labelHeight / bounds.height;  | 
|---|
 | 248 | +                double cellH = (double)getModel().getDisplay().getInt("timeline.item.height.min") / bounds.height;  | 
|---|
 | 249 | +                  | 
|---|
 | 250 | +                double maxCount = 0;  | 
|---|
 | 251 | +                  | 
|---|
 | 252 | +                // Set the minimum scale  | 
|---|
 | 253 | +                for (TimelineTrack t : trackList)  | 
|---|
 | 254 | +                {  | 
|---|
 | 255 | +                        //double height = t.size() * scale / (double) numShown;  | 
|---|
 | 256 | +  | 
|---|
 | 257 | +                        maxCount = Math.max(t.size(), maxCount);  | 
|---|
 | 258 | +                }  | 
|---|
 | 259 | +  | 
|---|
 | 260 | +                scale = Math.min(cellH * numShown, scale);  | 
|---|
 | 261 | +                scale = Math.max(min * numShown / maxCount, scale);  | 
|---|
 | 262 | +                  | 
|---|
 | 263 | +                double top = 0;  | 
|---|
 | 264 | +                for (TimelineTrack t : trackList)  | 
|---|
 | 265 | +                {  | 
|---|
 | 266 | +                        double height = Math.max(min, t.size() * scale / (double) numShown);  | 
|---|
 | 267 | +                        t.layout(top, height, this);  | 
|---|
 | 268 | +                        top += height;  | 
|---|
 | 269 | +                }  | 
|---|
 | 270 | +                fullHeight = (int) (top * bounds.height);  | 
|---|
 | 271 | +  | 
|---|
 | 272 | +                Collections.sort(trackList);  | 
|---|
 | 273 | +                frameChanged = false;  | 
|---|
 | 274 | +                  | 
|---|
 | 275 | +                return scale;  | 
|---|
 | 276 | +        }  | 
|---|
 | 277 | +  | 
|---|
 | 278 | +        private void updateVisualEncoding()  | 
|---|
 | 279 | +        {  | 
|---|
 | 280 | +                java.util.List<VisualAct> acts = encoder.apply();  | 
|---|
 | 281 | +  | 
|---|
 | 282 | +                // now arrange on tracks  | 
|---|
 | 283 | +                trackTable = new HashMap<String, TimelineTrack>();  | 
|---|
 | 284 | +                trackList = new ArrayList<TimelineTrack>();  | 
|---|
 | 285 | +                numShown = 0;  | 
|---|
 | 286 | +                for (VisualAct v : acts)  | 
|---|
 | 287 | +                {  | 
|---|
 | 288 | +                        if (!v.isVisible())  | 
|---|
 | 289 | +                        {  | 
|---|
 | 290 | +                                continue;  | 
|---|
 | 291 | +                        }  | 
|---|
 | 292 | +                        numShown++;  | 
|---|
 | 293 | +                        String s = v.getTrackString();  | 
|---|
 | 294 | +                        TimelineTrack t = trackTable.get(s);  | 
|---|
 | 295 | +                        if (t == null)  | 
|---|
 | 296 | +                        {  | 
|---|
 | 297 | +                                t = new TimelineTrack(s);  | 
|---|
 | 298 | +                                trackTable.put(s, t);  | 
|---|
 | 299 | +                                trackList.add(t);  | 
|---|
 | 300 | +                        }  | 
|---|
 | 301 | +                        t.add(v);  | 
|---|
 | 302 | +                        v.setTrack(t);  | 
|---|
 | 303 | +                }  | 
|---|
 | 304 | +  | 
|---|
 | 305 | +                /*  | 
|---|
 | 306 | +                // the following code is no longer used, but could come in handy again one day...  | 
|---|
 | 307 | +                  | 
|---|
 | 308 | +                // If there is more than one "small" track, then we will coalesce them into  | 
|---|
 | 309 | +                // one bigger "miscellaneous" track.  | 
|---|
 | 310 | +                int minSize=numShown/30;//Math.max(3,numShown/30);  | 
|---|
 | 311 | +                ArrayList<TimelineTrack> small=new ArrayList<TimelineTrack>();  | 
|---|
 | 312 | +                for (TimelineTrack t: trackList)  | 
|---|
 | 313 | +                {  | 
|---|
 | 314 | +                if (t.size()<minSize)  | 
|---|
 | 315 | +                small.add(t);  | 
|---|
 | 316 | +                }  | 
|---|
 | 317 | +                if (small.size()>1)  | 
|---|
 | 318 | +                {  | 
|---|
 | 319 | +                // create a new Track for "miscellaneous."  | 
|---|
 | 320 | +                TimelineTrack misc=new TimelineTrack(Display.MISC_CODE);  | 
|---|
 | 321 | +                trackList.add(misc);  | 
|---|
 | 322 | +                trackTable.put(misc.label, misc);  | 
|---|
 | 323 | +                  | 
|---|
 | 324 | +                // remove the old tracks.  | 
|---|
 | 325 | +                for (TimelineTrack t:small)  | 
|---|
 | 326 | +                {  | 
|---|
 | 327 | +                trackList.remove(t);  | 
|---|
 | 328 | +                trackTable.remove(t.label);  | 
|---|
 | 329 | +                for (VisualAct v: t.visualActs)  | 
|---|
 | 330 | +                {  | 
|---|
 | 331 | +                v.setTrack(misc);  | 
|---|
 | 332 | +                misc.add(v);  | 
|---|
 | 333 | +                }  | 
|---|
 | 334 | +                }  | 
|---|
 | 335 | +                // sort miscellaneous items in time order.  | 
|---|
 | 336 | +                //Collections.sort(misc.visualActs);  | 
|---|
 | 337 | +                }  | 
|---|
 | 338 | +                 */  | 
|---|
 | 339 | +  | 
|---|
 | 340 | +                for (TimelineTrack t : trackList)  | 
|---|
 | 341 | +                {  | 
|---|
 | 342 | +                        Collections.sort(t.visualActs);  | 
|---|
 | 343 | +                }  | 
|---|
 | 344 | +        }  | 
|---|
 | 345 | +}  | 
|---|