package org.geogebra.desktop.export; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.text.NumberFormat; import java.util.Locale; import java.util.Vector; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import org.geogebra.common.euclidian.EuclidianView; import org.geogebra.common.main.Localization; import org.geogebra.common.util.debug.Log; import org.geogebra.common.util.lang.Unicode; import org.geogebra.desktop.gui.inputfield.MyTextFieldD; import org.geogebra.desktop.main.AppD; /** * Panel for print scale of EuclidianView. Notifies attached ActionListeners * about scale changes. * * @author Markus Hohenwarter */ public class PrintScalePanel extends JPanel { private static final long serialVersionUID = 1L; private static final int maxFracDigits = 5; private JTextField tfScale1, tfScale2, tfSize1, tfSize2; private JTextField tfScaleFixed; private Vector<ActionListener> listeners = new Vector<ActionListener>(); private EuclidianView ev; private NumberFormat nf; @SuppressWarnings("rawtypes") private JComboBox exportMode; private JPanel pxModePanel, cmModePanel; private JPanel fixedSizeModePanel; public enum PrintScaleModes { SIZEINCM, SIZEINPX, FIXED_SIZE } private PrintScaleModes mode = PrintScaleModes.SIZEINCM; private boolean pixelSizeEnabled = true; private String jcbItemSizeInPixels = ""; private String jcbItemFixedSize = ""; private String jcbItemScaleInCentimeter = ""; /** * @param app * application * @param ev * selected view */ @SuppressWarnings({ "unchecked", "rawtypes" }) public PrintScalePanel(AppD app, EuclidianView ev) { this.ev = ev; Localization loc = app.getLocalization(); nf = NumberFormat.getInstance(Locale.ENGLISH); nf.setMaximumFractionDigits(maxFracDigits); nf.setGroupingUsed(false); setLayout(new FlowLayout(FlowLayout.LEFT)); Runnable updateCm = new Runnable() { @Override public void run() { fireTextFieldUpdate(); } }; Runnable updateFixedSize = new Runnable() { @Override public void run() { fireFixedSizeTextFieldUpdate(); } }; Runnable updateWidth = new Runnable() { @Override public void run() { fireWidthTextFieldUpdate(); } }; Runnable updateHeight = new Runnable() { @Override public void run() { fireHeightTextFieldUpdate(); } }; tfScale1 = getNumberField(app, updateCm); tfScale2 = getNumberField(app, updateCm); tfSize1 = getNumberField(app, updateWidth); tfSize2 = getNumberField(app, updateHeight); tfScaleFixed = getNumberField(app, updateFixedSize); // this label is not used (replaced with combo box exportMode) // scaleLabel = new JLabel(loc.getPlain("ScaleInCentimeter") + ":"); // new variables added (3 rows) - are used as items in the combo box // exportMode jcbItemScaleInCentimeter = loc.getPlain("ScaleInCentimeter") + ":"; jcbItemFixedSize = loc.getPlain("FixedSize") + ":"; jcbItemSizeInPixels = loc.getPlain("SizeInPixels") + ":"; exportMode = new JComboBox(); exportMode.addItem(jcbItemScaleInCentimeter); exportMode.addItem(jcbItemFixedSize); exportMode.addItem(jcbItemSizeInPixels); // end of block update add(exportMode); exportMode.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { switchMode(); } }); fixedSizeModePanel = new JPanel(); fixedSizeModePanel.setLayout(new FlowLayout(FlowLayout.LEFT)); fixedSizeModePanel.add(new JLabel( " " + loc.getPlain("APixelsOnScreen", "100") + " = ")); fixedSizeModePanel.add(tfScaleFixed); fixedSizeModePanel.add(new JLabel(" cm")); cmModePanel = new JPanel(); cmModePanel.setLayout(new FlowLayout(FlowLayout.LEFT)); cmModePanel.add(tfScale1); cmModePanel.add(new JLabel(" " + loc.getPlain("units") + " = ")); cmModePanel.add(tfScale2); cmModePanel.add(new JLabel(" cm")); pxModePanel = new JPanel(); pxModePanel.setLayout(new FlowLayout(FlowLayout.LEFT)); pxModePanel.add(tfSize1); pxModePanel.add(new JLabel( loc.getMenu("Pixels.short") + " " + Unicode.MULTIPLY + " ")); pxModePanel.add(tfSize2); pxModePanel.add(new JLabel(loc.getMenu("Pixels.short"))); add(cmModePanel); updateScaleTextFields(); } /** * @param b */ @SuppressWarnings("unchecked") public void enableAbsoluteSize(boolean b) { if (b == pixelSizeEnabled) { return; } pixelSizeEnabled = b; exportMode.removeItem(jcbItemSizeInPixels); // this.removeAll(); if (b) { // this.add(exportMode); exportMode.addItem(jcbItemSizeInPixels); } else { // this.add(scaleLabel); } // this.add(cmModePanel); } /** * Switch to the correct mode (pixel vs cm vs fixed size) */ void switchMode() { if (exportMode.getSelectedItem().toString() .equals(jcbItemSizeInPixels)) { mode = PrintScaleModes.SIZEINPX; } else if (exportMode.getSelectedItem().toString() .equals(jcbItemFixedSize)) { mode = PrintScaleModes.FIXED_SIZE; } else { mode = PrintScaleModes.SIZEINCM; } switch (mode) { case SIZEINCM: PrintScalePanel.this.remove(pxModePanel); PrintScalePanel.this.remove(fixedSizeModePanel); PrintScalePanel.this.add(cmModePanel); updateScaleTextFields(); break; case SIZEINPX: PrintScalePanel.this.remove(cmModePanel); PrintScalePanel.this.remove(fixedSizeModePanel); PrintScalePanel.this.add(pxModePanel); updateSizeTextFields(ev.getExportWidth(), ev.getExportHeight()); break; case FIXED_SIZE: PrintScalePanel.this.remove(cmModePanel); PrintScalePanel.this.remove(pxModePanel); PrintScalePanel.this.add(fixedSizeModePanel); updateFixedSizeTextFields(); revalidate(); repaint(); break; } SwingUtilities.updateComponentTreeUI(PrintScalePanel.this); notifyListeners(); } private static JTextField getNumberField(AppD app, final Runnable run) { JTextField ret = new MyTextFieldD(app); ret.setColumns(maxFracDigits); ret.setHorizontalAlignment(SwingConstants.RIGHT); FocusListener flst = new FocusListener() { @Override public void focusLost(FocusEvent e) { run.run(); } @Override public void focusGained(FocusEvent e) { // } }; ActionListener al = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { run.run(); } }; ret.addActionListener(al); ret.addFocusListener(flst); return ret; } /** * Update pixel fields to default values * * @param width * width * @param height * height */ private void updateSizeTextFields(int width, int height) { setTextNoListener(tfSize1, nf.format(width)); setTextNoListener(tfSize2, nf.format(height)); } private void updateFixedSizeTextFields() { double relScale = 100 * ev.getPrintingScale() / ev.getXscale(); setTextNoListener(tfScaleFixed, nf.format(relScale)); } private void updateScaleTextFields() { double scale = ev.getPrintingScale(); if (scale <= 1) { setTextNoListener(tfScale2, "1"); setTextNoListener(tfScale1, nf.format(1 / scale)); } else { setTextNoListener(tfScale2, nf.format(scale)); setTextNoListener(tfScale1, "1"); } } private static void setTextNoListener(JTextField field, String s) { ActionListener ret = field.getActionListeners()[0]; field.removeActionListener(ret); field.setText(s); field.addActionListener(ret); } /** * Validate the texts in scale input, if OK, change export scale of EV and * notify listeners */ void fireTextFieldUpdate() { boolean viewChanged = false; try { double numerator = Double.parseDouble(tfScale2.getText()); double denominator = Double.parseDouble(tfScale1.getText()); double scale = numerator / denominator; if (!(Double.isInfinite(scale) || Double.isNaN(scale))) { ev.setPrintingScale(scale); viewChanged = true; } } catch (Exception e) { // invalid numbers, continue editing } updateScaleTextFields(); if (viewChanged) { notifyListeners(); } } void fireWidthTextFieldUpdate() { try { int width = Integer.parseInt(tfSize1.getText()); int height = (width * ev.getExportHeight()) / ev.getExportWidth(); updateSizeTextFields(width, height); notifyListeners(); } catch (Exception e) { Log.debug(tfSize1.getText() + " is not a valid number"); } } void fireHeightTextFieldUpdate() { try { int height = Integer.parseInt(tfSize2.getText()); int width = (height * ev.getExportWidth()) / ev.getExportHeight(); updateSizeTextFields(width, height); notifyListeners(); } catch (Exception e) { Log.debug(tfSize2.getText() + " is not a valid number"); } } void fireFixedSizeTextFieldUpdate() { boolean viewChanged = false; try { double userScale = Double.parseDouble(tfScaleFixed.getText()); if (!(Double.isInfinite(userScale) || Double.isNaN(userScale))) { double scale = userScale * ev.getXscale() / 100; ev.setPrintingScale(scale); viewChanged = true; } } catch (Exception e) { e.printStackTrace(); } updateFixedSizeTextFields(); if (viewChanged) { notifyListeners(); } } /** * @param lst * listens to changes of scale / size */ public void addActionListener(ActionListener lst) { listeners.add(lst); } private void notifyListeners() { int size = listeners.size(); for (int i = 0; i < size; i++) { listeners.get(i).actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "ViewChanged")); } } /** * @return width in pixels */ public int getPixelWidth() { try { return Integer.parseInt(tfSize1.getText()); } catch (Exception e) { return -1; } } /** * Height in pixels */ public int getPixelHeight() { try { return Integer.parseInt(tfSize2.getText()); } catch (Exception e) { return -1; } } /** * @return whether we export using pixels rather than cm */ public PrintScaleModes getMode() { return this.mode; } }