Java tutorial
/* * Copyright 2011, 2012 Delving BV * * Licensed under the EUPL, Version 1.0 or? as soon they * will be approved by the European Commission - subsequent * versions of the EUPL (the "Licence"); * you may not use this work except in compliance with the * Licence. * You may obtain a copy of the Licence at: * * http://ec.europa.eu/idabc/eupl * * Unless required by applicable law or agreed to in * writing, software distributed under the Licence is * distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. * See the Licence for the specific language governing * permissions and limitations under the Licence. */ package eu.delving.sip.frames; import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamAsAttribute; import com.thoughtworks.xstream.annotations.XStreamImplicit; import eu.delving.XStreamFactory; import eu.delving.sip.base.FrameBase; import eu.delving.sip.base.Swing; import eu.delving.sip.base.Work; import eu.delving.sip.files.Storage; import eu.delving.sip.model.SipModel; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.WordUtils; import javax.swing.*; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.event.ActionEvent; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import static eu.delving.sip.base.FrameBase.INSETS; import static eu.delving.sip.base.KeystrokeHelper.SPACE; import static eu.delving.sip.base.KeystrokeHelper.addKeyboardAction; import static eu.delving.sip.base.KeystrokeHelper.menuDigit; import static eu.delving.sip.base.SwingHelper.scrollV; import static eu.delving.sip.frames.AllFrames.View.BIG_PICTURE; import static eu.delving.sip.frames.AllFrames.View.CLEAR; import static eu.delving.sip.frames.AllFrames.View.DATA_SETS; import static eu.delving.sip.frames.AllFrames.View.DECADENT_DISPLAY; import static eu.delving.sip.frames.AllFrames.View.QUICK_MAPPING; /** * Hold on to all the frames and manage their arrangement on the desktop. It is possible to make adjustments in * how the frames are arranged in the different views because the default arrangements are copied into a file the * data directory and can be modified for subsequent runs of the program. Useful changes should be copied * back into the default XML file eventually. * * @author Gerald de Jong */ public class AllFrames { private Dimension LARGE_ICON_SIZE = new Dimension(80, 50); private JPanel content; private FrameBase[] frames; private FrameBase dataSetFrame; private FrameArrangements frameArrangements; private List<Arrangement> arrangements = new ArrayList<Arrangement>(); private View currentView = CLEAR; private SipModel sipModel; private ViewSelector viewSelector = new ViewSelector() { @Override public void selectView(View view) { for (Arrangement arrangement : arrangements) { if (arrangement.source.view == view) { arrangement.actionPerformed(null); currentView = arrangement.source.view; return; } } for (FrameBase frame : frames) frame.closeFrame(); currentView = CLEAR; } @Override public void refreshView() { selectView(currentView); } }; public enum View { CLEAR, DATA_SETS, QUICK_MAPPING, BIG_PICTURE, CODE_TWEAKING, DEEP_DELVING, DECADENT_DISPLAY, FUNCTIONS, STATISTICS, REPORT, MAPPING_CODE, LOG; public String getHtml() { return WordUtils.capitalizeFully(super.toString().replaceAll("_", " ")); } } public interface ViewSelector { void selectView(View view); void refreshView(); } public AllFrames(final SipModel sipModel, JPanel content, FrameBase dataSetFrame, FrameBase logFrame) { this.sipModel = sipModel; this.content = content; this.dataSetFrame = dataSetFrame; sipModel.setViewSelector(viewSelector); FunctionFrame functionFrame = new FunctionFrame(sipModel); MappingCodeFrame mappingCodeFrame = new MappingCodeFrame(sipModel); StatsFrame statsFrame = new StatsFrame(sipModel); CreateFrame create = new CreateFrame(sipModel); Action createAction = create.getCreateMappingAction(); addKeyboardAction(createAction, SPACE, create); FrameBase source = new SourceFrame(sipModel); addKeyboardAction(createAction, SPACE, source); TargetFrame target = new TargetFrame(sipModel); addKeyboardAction(createAction, SPACE, target); FrameBase input = new InputFrame(sipModel); FrameBase recMapping = new RecMappingFrame(sipModel); FrameBase fieldMapping = new FieldMappingFrame(sipModel); FrameBase output = new OutputFrame(sipModel); FrameBase reportFrame = new ReportFrame(sipModel); this.frames = new FrameBase[] { dataSetFrame, source, create, target, input, recMapping, fieldMapping, output, functionFrame, statsFrame, reportFrame, mappingCodeFrame, logFrame }; try { File file = frameArrangementsFile(); createDefaultFrameArrangements(file); try { addFrameArrangements(new FileInputStream(file)); } catch (Exception e) { // when you want to adjust, remove the line above and put it here: createDefaultFrameArrangements(file); addFrameArrangements(new FileInputStream(file)); } } catch (IOException e) { throw new RuntimeException("Initializing views", e); } } private File frameArrangementsFile() { return this.sipModel.getStorage().cache(Storage.FRAME_ARRANGEMENTS_FILE); } private void createDefaultFrameArrangements(File file) throws IOException { List<String> lines = IOUtils.readLines(getClass().getResource("/frame-arrangements.xml").openStream()); IOUtils.writeLines(lines, "\n", new FileOutputStream(file)); } private void addFrameArrangements(InputStream inputStream) { frameArrangements = (FrameArrangements) XStreamFactory.getStreamFor(FrameArrangements.class) .fromXML(inputStream); int viewIndex = 0; for (XArrangement view : frameArrangements.arrangements) { Arrangement arrangement = new Arrangement(view, viewIndex++); for (XFrame frame : view.frames) arrangement.blocks.add(new Block(frame)); arrangements.add(arrangement); arrangement.source = view; } } public FrameBase[] getFrames() { return frames; } public ViewSelector getViewSelector() { return viewSelector; } public Swing prepareForNothing() { return new Swing() { @Override public void run() { viewSelector.selectView(CLEAR); } }; } public Swing prepareForMapping(final JComponent component) { return new Swing() { @Override public void run() { viewSelector.selectView(component.getSize().width > 1600 ? DECADENT_DISPLAY : QUICK_MAPPING); } }; } public Swing prepareForInvestigation(final JComponent component) { return new Swing() { @Override public void run() { viewSelector.selectView(component.getSize().width > 1600 ? DECADENT_DISPLAY : BIG_PICTURE); } }; } public Swing prepareForDelimiting() { return new Swing() { @Override public void run() { viewSelector.selectView(QUICK_MAPPING); } }; } public void initiate() { sipModel.exec(new Swing() { @Override public void run() { viewSelector.selectView(DATA_SETS); dataSetFrame.refresh(); } }); } public JMenu getViewMenu() { JMenu menu = new JMenu("View"); for (Action action : arrangements) menu.add(action); return menu; } public JComponent getSidePanel() { JPanel arrangements = new JPanel(); arrangements.setLayout(new BoxLayout(arrangements, BoxLayout.Y_AXIS)); for (Arrangement a : this.arrangements) { JButton b = new JButton(a); b.setHorizontalTextPosition(JButton.CENTER); b.setVerticalTextPosition(JButton.BOTTOM); b.setFont(new Font("Sans", Font.PLAIN, 10)); arrangements.add(b); arrangements.add(Box.createVerticalStrut(5)); } arrangements.add(Box.createVerticalGlue()); JPanel p = new JPanel(new BorderLayout()); p.add(arrangements, BorderLayout.CENTER); return scrollV(p); } public static JComponent miniScrollV(String title, JComponent content) { JPanel p = new JPanel(new BorderLayout()); p.setBorder(BorderFactory.createTitledBorder(title)); JScrollPane scroll = new JScrollPane(content); scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER); scroll.setPreferredSize(new Dimension(200, 80)); p.add(scroll); return p; } private FrameBase frame(FrameBase.Which which) { for (FrameBase frame : frames) if (frame.getWhich() == which) return frame; throw new RuntimeException(which + " not found"); } private static class Situation implements FrameBase.Placement { private Point location; private Dimension size; private Situation(Point location, Dimension size) { this.location = location; this.size = size; } public Point getLocation() { return location; } public Dimension getSize() { return size; } } private class Block { XFrame original; FrameBase frame; private Block(XFrame frame) { this.original = frame; this.frame = frame(frame.which); } Situation situate(Dimension all, int rows, int cols, boolean useInsets) { int wx = all.width / cols - (all.width % 2); int hx = all.height / rows - (all.height % 2); if (useInsets) { return new Situation( new Point(original.getX() * wx - INSETS.left, original.getY() * hx - INSETS.top), new Dimension(original.getW() * wx + INSETS.left + INSETS.right, original.getH() * hx + INSETS.top + INSETS.bottom)); } else { return new Situation(new Point(original.getX() * wx, original.getY() * hx), new Dimension(original.getW() * wx, original.getH() * hx)); } } int rows(int max) { return Math.max(max, original.getY() + original.getH()); } int cols(int max) { return Math.max(max, original.getX() + original.getW()); } } private class Arrangement extends AbstractAction implements Swing { List<Block> blocks = new ArrayList<Block>(); public XArrangement source; private ViewIcon icon; Arrangement(XArrangement source, int viewIndex) { super(source.view.getHtml()); if (viewIndex < 9) { putValue(ACCELERATOR_KEY, menuDigit(viewIndex + 1)); } icon = new ViewIcon(this, LARGE_ICON_SIZE); putValue(Action.LARGE_ICON_KEY, icon); } @Override public void actionPerformed(ActionEvent actionEvent) { int rows = 0; int cols = 0; for (Block block : blocks) { rows = block.rows(rows); cols = block.cols(cols); } for (FrameBase frame : frames) frame.closeFrame(); for (Block block : blocks) { Situation situation = block.situate(sipModel.getDesktop().getSize(), rows, cols, true); block.frame.setPlacement(situation); } for (Block block : blocks) block.frame.openFrame(); for (FrameBase base : frames) base.setArrangementSource(source, this); currentView = source.view; content.requestFocus(); } public String toString() { return (String) this.getValue(Action.NAME); } @Override public void run() { actionPerformed(null); sipModel.exec(new Work() { @Override public void run() { try { XStreamFactory.getStreamFor(FrameArrangements.class).toXML(frameArrangements, new FileOutputStream(frameArrangementsFile())); } catch (FileNotFoundException e) { // eat it. } } @Override public Job getJob() { return Job.READ_FRAME_ARRANGEMENTS; } }); } } private static class ViewIcon implements Icon { private Arrangement a; private Dimension size; private Component component; private ViewIcon(Arrangement a, Dimension size) { this.a = a; this.size = size; } @Override public void paintIcon(Component component, Graphics graphics, int x, int y) { this.component = component; Graphics2D g = (Graphics2D) graphics; g.setColor(Color.GRAY); int rows = 0; int cols = 0; for (Block block : a.blocks) { rows = block.rows(rows); cols = block.cols(cols); } for (Block block : a.blocks) { Situation situation = block.situate(size, rows, cols, false); g.drawRect(x + situation.getLocation().x + 1, y + situation.getLocation().y + 1, situation.getSize().width, situation.getSize().height); } } @Override public int getIconWidth() { return size.width + 4; } @Override public int getIconHeight() { return size.height + 4; } public void refresh() { component.repaint(); } } @XStreamAlias("frame-arrangements") public static class FrameArrangements { @XStreamImplicit List<XArrangement> arrangements; } @XStreamAlias("arrangement") public static class XArrangement { @XStreamAsAttribute public View view; @XStreamImplicit public List<XFrame> frames; } @XStreamAlias("frame") public static class XFrame { @XStreamAsAttribute public String where; @XStreamAsAttribute public FrameBase.Which which; public int getX() { return locVal(0); } public int getY() { return locVal(1); } public int getW() { return locVal(2); } public int getH() { return locVal(3); } private int locVal(int index) { return where.charAt(index) - '0'; } } }