/* * PageSetupPanel.java 27 aout 07 * * Sweet Home 3D, Copyright (c) 2007 Emmanuel PUYBARET / eTeks <info@eteks.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package com.eteks.sweethome3d.swing; import java.awt.Component; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.KeyboardFocusManager; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.print.PageFormat; import java.awt.print.Paper; import java.awt.print.PrinterJob; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.security.AccessControlException; import javax.swing.ActionMap; import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JSeparator; import javax.swing.JSpinner; import javax.swing.JTextField; import javax.swing.JToolBar; import javax.swing.KeyStroke; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import com.eteks.sweethome3d.j3d.Component3DManager; import com.eteks.sweethome3d.model.HomePrint; import com.eteks.sweethome3d.model.UserPreferences; import com.eteks.sweethome3d.swing.NullableSpinner.NullableSpinnerNumberModel; import com.eteks.sweethome3d.tools.OperatingSystem; import com.eteks.sweethome3d.viewcontroller.DialogView; import com.eteks.sweethome3d.viewcontroller.PageSetupController; import com.eteks.sweethome3d.viewcontroller.View; /** * Home page setup editing panel. * @author Emmanuel Puybaret */ public class PageSetupPanel extends JPanel implements DialogView { private static final int DEFAULT_SCALE = 100; private final PageSetupController controller; private PageFormat pageFormat; private JButton pageFormatButton; private JCheckBox furniturePrintedCheckBox; private JCheckBox planPrintedCheckBox; private JRadioButton bestFitPlanScaleRadioButton; private JRadioButton userPlanScaleRadioButton; private JSpinner userPlanScaleSpinner; private JCheckBox view3DPrintedCheckBox; private JLabel headerFormatLabel; private JTextField headerFormatTextField; private JLabel footerFormatLabel; private JTextField footerFormatTextField; private JLabel variablesLabel; private JToolBar variableButtonsToolBar; private String dialogTitle; /** * Creates a panel that displays page setup. * @param controller the controller of this panel */ public PageSetupPanel(UserPreferences preferences, PageSetupController controller) { super(new GridBagLayout()); this.controller = controller; createActions(preferences); createComponents(preferences, controller); setMnemonics(preferences); layoutComponents(); } /** * Creates actions for variables. */ private void createActions(UserPreferences preferences) { ActionMap actions = getActionMap(); for (final HomePrintableComponent.Variable variable : HomePrintableComponent.Variable.values()) { actions.put(variable, new ResourceAction(preferences, PageSetupPanel.class, "INSERT_" + variable.name()) { @Override public void actionPerformed(ActionEvent ev) { insertVariable(variable.getUserCode()); } }); } } /** * Inserts a code in the text field that has the focus and selects it. */ private void insertVariable(String userCode) { Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); if (focusOwner instanceof JTextField) { JTextField textField = (JTextField)focusOwner; textField.replaceSelection(userCode); int lastCharacter = textField.getCaretPosition(); int firstCharacter = lastCharacter - userCode.length(); textField.select(firstCharacter, lastCharacter); } } /** * Creates and initializes components. */ private void createComponents(UserPreferences preferences, final PageSetupController controller) { this.pageFormatButton = new JButton(SwingTools.getLocalizedLabelText(preferences, PageSetupPanel.class, "pageFormatButton.text")); this.furniturePrintedCheckBox = new JCheckBox(SwingTools.getLocalizedLabelText(preferences, PageSetupPanel.class, "furniturePrintedCheckBox.text")); this.planPrintedCheckBox = new JCheckBox(SwingTools.getLocalizedLabelText(preferences, PageSetupPanel.class, "planPrintedCheckBox.text")); // Create scale radio buttons and user's scale spinner this.bestFitPlanScaleRadioButton = new JRadioButton(SwingTools.getLocalizedLabelText(preferences, PageSetupPanel.class, "bestFitPlanScaleRadioButton.text")); this.userPlanScaleRadioButton = new JRadioButton(SwingTools.getLocalizedLabelText(preferences, PageSetupPanel.class, "userPlanScaleRadioButton.text")); ButtonGroup scaleButtonsGroup = new ButtonGroup(); scaleButtonsGroup.add(this.bestFitPlanScaleRadioButton); scaleButtonsGroup.add(this.userPlanScaleRadioButton); final NullableSpinner.NullableSpinnerNumberModel userPlanScaleSpinnerModel = new NullableSpinner.NullableSpinnerNumberModel(10, 1, 10000, 10); this.userPlanScaleSpinner = new AutoCommitSpinner(userPlanScaleSpinnerModel); this.view3DPrintedCheckBox = new JCheckBox(SwingTools.getLocalizedLabelText(preferences, PageSetupPanel.class, "view3DPrintedCheckBox.text")); this.headerFormatLabel = new JLabel(SwingTools.getLocalizedLabelText(preferences, PageSetupPanel.class, "headerFormatLabel.text")); this.headerFormatTextField = new JTextField(20); if (!OperatingSystem.isMacOSXLeopardOrSuperior()) { SwingTools.addAutoSelectionOnFocusGain(this.headerFormatTextField); } this.footerFormatLabel = new JLabel(SwingTools.getLocalizedLabelText(preferences, PageSetupPanel.class, "footerFormatLabel.text")); this.footerFormatTextField = new JTextField(20); if (!OperatingSystem.isMacOSXLeopardOrSuperior()) { SwingTools.addAutoSelectionOnFocusGain(this.footerFormatTextField); } // Create variables buttons tool bar this.variablesLabel = new JLabel(preferences.getLocalizedString( PageSetupPanel.class, "variablesLabel.text")); this.variableButtonsToolBar = new JToolBar(); this.variableButtonsToolBar.setFloatable(false); ActionMap actions = getActionMap(); this.variableButtonsToolBar.add(actions.get(HomePrintableComponent.Variable.PAGE_NUMBER)); this.variableButtonsToolBar.add(actions.get(HomePrintableComponent.Variable.PAGE_COUNT)); this.variableButtonsToolBar.add(actions.get(HomePrintableComponent.Variable.PLAN_SCALE)); this.variableButtonsToolBar.add(actions.get(HomePrintableComponent.Variable.DATE)); this.variableButtonsToolBar.add(actions.get(HomePrintableComponent.Variable.TIME)); this.variableButtonsToolBar.add(actions.get(HomePrintableComponent.Variable.HOME_PRESENTATION_NAME)); this.variableButtonsToolBar.add(actions.get(HomePrintableComponent.Variable.HOME_NAME)); for (int i = 0, n = this.variableButtonsToolBar.getComponentCount(); i < n; i++) { JComponent component = (JComponent)this.variableButtonsToolBar.getComponentAtIndex(i); // Remove focusable property on buttons component.setFocusable(false); } updateComponents(controller.getPrint()); final PropertyChangeListener printChangeListener = new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent ev) { updateComponents(controller.getPrint()); } }; this.pageFormatButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ev) { // Show the page setup dialog PrinterJob printerJob = PrinterJob.getPrinterJob(); pageFormat = printerJob.pageDialog(pageFormat); updateController(controller); } }); ItemListener itemListener = new ItemListener() { public void itemStateChanged(ItemEvent ev) { updateController(controller); } }; this.furniturePrintedCheckBox.addItemListener(itemListener); this.planPrintedCheckBox.addItemListener(itemListener); userPlanScaleSpinnerModel.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent ev) { updateController(controller); } }); this.bestFitPlanScaleRadioButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ev) { updateController(controller); } }); this.userPlanScaleRadioButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ev) { if (userPlanScaleRadioButton.isSelected() && userPlanScaleSpinnerModel.getValue() == null) { userPlanScaleSpinnerModel.setValue(DEFAULT_SCALE); } else { updateController(controller); } } }); this.view3DPrintedCheckBox.addItemListener(itemListener); controller.addPropertyChangeListener(PageSetupController.Property.PRINT, printChangeListener); DocumentListener documentListener = new DocumentListener() { public void changedUpdate(DocumentEvent ev) { controller.removePropertyChangeListener(PageSetupController.Property.PRINT, printChangeListener); updateController(controller); controller.addPropertyChangeListener(PageSetupController.Property.PRINT, printChangeListener); } public void insertUpdate(DocumentEvent ev) { changedUpdate(ev); } public void removeUpdate(DocumentEvent ev) { changedUpdate(ev); } }; this.headerFormatTextField.getDocument().addDocumentListener(documentListener); FocusListener textFieldFocusListener = new FocusListener() { public void focusGained(FocusEvent ev) { ActionMap actionMap = getActionMap(); for (HomePrintableComponent.Variable field : HomePrintableComponent.Variable.values()) { actionMap.get(field).setEnabled(true); } } public void focusLost(FocusEvent ev) { ActionMap actionMap = getActionMap(); for (HomePrintableComponent.Variable field : HomePrintableComponent.Variable.values()) { actionMap.get(field).setEnabled(false); } } }; this.headerFormatTextField.addFocusListener(textFieldFocusListener); this.footerFormatTextField.getDocument().addDocumentListener(documentListener); this.footerFormatTextField.addFocusListener(textFieldFocusListener); this.dialogTitle = preferences.getLocalizedString(PageSetupPanel.class, "pageSetup.title"); } /** * Updates components from <code>homePrint</code> attributes. */ private void updateComponents(HomePrint homePrint) { this.pageFormat = HomePrintableComponent.getPageFormat(homePrint); boolean no3D; try { no3D = Boolean.getBoolean("com.eteks.sweethome3d.no3D"); } catch (AccessControlException ex) { // If com.eteks.sweethome3d.no3D property can't be read, // security manager won't allow to access to Java 3D DLLs required to print 3D images too no3D = true; } // Check if off screen image is supported boolean offscreenCanvas3DSupported; if (no3D) { offscreenCanvas3DSupported = false; } else { offscreenCanvas3DSupported = Component3DManager.getInstance().isOffScreenImageSupported(); } final NullableSpinnerNumberModel userPlanScaleSpinnerModel = (NullableSpinner.NullableSpinnerNumberModel)this.userPlanScaleSpinner.getModel(); if (homePrint != null) { this.furniturePrintedCheckBox.setSelected(homePrint.isFurniturePrinted()); this.planPrintedCheckBox.setSelected(homePrint.isPlanPrinted()); this.bestFitPlanScaleRadioButton.setEnabled(homePrint.isPlanPrinted()); this.userPlanScaleRadioButton.setEnabled(homePrint.isPlanPrinted()); if (homePrint.getPlanScale() == null) { this.bestFitPlanScaleRadioButton.setSelected(true); } else { this.userPlanScaleRadioButton.setSelected(true); } this.userPlanScaleSpinner.setEnabled(homePrint.isPlanPrinted() && homePrint.getPlanScale() != null); this.view3DPrintedCheckBox.setSelected(homePrint.isView3DPrinted() && offscreenCanvas3DSupported); userPlanScaleSpinnerModel.setNullable(homePrint.getPlanScale() == null); userPlanScaleSpinnerModel.setValue(homePrint.getPlanScale() != null ? new Integer(Math.round(1 / homePrint.getPlanScale())) : null); String headerFormat = homePrint.getHeaderFormat(); this.headerFormatTextField.setText(headerFormat != null ? headerFormat : ""); String footerFormat = homePrint.getFooterFormat(); this.footerFormatTextField.setText(footerFormat != null ? footerFormat : ""); } else { this.furniturePrintedCheckBox.setSelected(true); this.planPrintedCheckBox.setSelected(true); this.bestFitPlanScaleRadioButton.setEnabled(true); this.bestFitPlanScaleRadioButton.setSelected(true); this.userPlanScaleRadioButton.setEnabled(true); this.userPlanScaleSpinner.setEnabled(false); this.view3DPrintedCheckBox.setSelected(offscreenCanvas3DSupported); userPlanScaleSpinnerModel.setNullable(true); userPlanScaleSpinnerModel.setValue(null); this.headerFormatTextField.setText(""); this.footerFormatTextField.setText(""); } this.view3DPrintedCheckBox.setEnabled(offscreenCanvas3DSupported); this.view3DPrintedCheckBox.setVisible(!no3D); } /** * Updates controller print attributes. */ public void updateController(PageSetupController controller) { // Return an HomePrint instance matching returnedPageFormat HomePrint.PaperOrientation paperOrientation; switch (this.pageFormat.getOrientation()) { case PageFormat.LANDSCAPE : paperOrientation = HomePrint.PaperOrientation.LANDSCAPE; break; case PageFormat.REVERSE_LANDSCAPE : paperOrientation = HomePrint.PaperOrientation.REVERSE_LANDSCAPE; break; default : paperOrientation = HomePrint.PaperOrientation.PORTRAIT; break; } Paper paper = this.pageFormat.getPaper(); HomePrint homePrint = new HomePrint(paperOrientation, (float)paper.getWidth(), (float)paper.getHeight(), (float)paper.getImageableY(), (float)paper.getImageableX(), (float)(paper.getHeight() - paper.getImageableHeight() - paper.getImageableY()), (float)(paper.getWidth() - paper.getImageableWidth() - paper.getImageableX()), this.furniturePrintedCheckBox.isSelected(), this.planPrintedCheckBox.isSelected(), this.view3DPrintedCheckBox.isSelected(), this.userPlanScaleRadioButton.isSelected() && this.userPlanScaleSpinner.getValue() != null ? 1f / ((Number)this.userPlanScaleSpinner.getValue()).intValue() : null, this.headerFormatTextField.getText().trim(), this.footerFormatTextField.getText().trim()); controller.setPrint(homePrint); } /** * Sets components mnemonics and label / component associations. */ private void setMnemonics(UserPreferences preferences) { if (!OperatingSystem.isMacOSX()) { this.pageFormatButton.setMnemonic(KeyStroke.getKeyStroke(preferences.getLocalizedString( PageSetupPanel.class, "pageFormatButton.mnemonic")).getKeyCode()); this.furniturePrintedCheckBox.setMnemonic(KeyStroke.getKeyStroke(preferences.getLocalizedString( PageSetupPanel.class, "furniturePrintedCheckBox.mnemonic")).getKeyCode()); this.planPrintedCheckBox.setMnemonic(KeyStroke.getKeyStroke(preferences.getLocalizedString( PageSetupPanel.class, "planPrintedCheckBox.mnemonic")).getKeyCode()); this.view3DPrintedCheckBox.setMnemonic(KeyStroke.getKeyStroke(preferences.getLocalizedString( PageSetupPanel.class, "view3DPrintedCheckBox.mnemonic")).getKeyCode()); this.bestFitPlanScaleRadioButton.setMnemonic(KeyStroke.getKeyStroke(preferences.getLocalizedString( PageSetupPanel.class, "bestFitPlanScaleRadioButton.mnemonic")).getKeyCode()); this.userPlanScaleRadioButton.setMnemonic(KeyStroke.getKeyStroke(preferences.getLocalizedString( PageSetupPanel.class, "userPlanScaleRadioButton.mnemonic")).getKeyCode()); this.headerFormatLabel.setDisplayedMnemonic(KeyStroke.getKeyStroke(preferences.getLocalizedString( PageSetupPanel.class, "headerFormatLabel.mnemonic")).getKeyCode()); this.headerFormatLabel.setLabelFor(this.headerFormatTextField); this.footerFormatLabel.setDisplayedMnemonic(KeyStroke.getKeyStroke(preferences.getLocalizedString( PageSetupPanel.class, "footerFormatLabel.mnemonic")).getKeyCode()); this.footerFormatLabel.setLabelFor(this.footerFormatTextField); } } /** * Layouts panel components in panel with their labels. */ private void layoutComponents() { int labelAlignment = OperatingSystem.isMacOSX() ? GridBagConstraints.LINE_END : GridBagConstraints.LINE_START; // First row JPanel topPanel = new JPanel(new GridBagLayout()); topPanel.add(this.pageFormatButton, new GridBagConstraints( 0, 0, 2, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 10, 0) , 0, 0)); Insets lastComponentInsets = new Insets(0, 0, 5, 0); // Furniture component topPanel.add(this.furniturePrintedCheckBox, new GridBagConstraints( 0, 1, 2, 1, 0, 0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, lastComponentInsets , 0, 0)); // Plan components topPanel.add(this.planPrintedCheckBox, new GridBagConstraints( 0, 2, 2, 1, 0, 0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 2, 0), 0, 0)); topPanel.add(this.bestFitPlanScaleRadioButton, new GridBagConstraints( 0, 3, 2, 1, 0, 0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 20, 2, 0), 0, 0)); topPanel.add(this.userPlanScaleRadioButton, new GridBagConstraints( 0, 4, 1, 1, 0, 0, labelAlignment, GridBagConstraints.NONE, new Insets(0, 20, 5, 0), 0, 0)); topPanel.add(this.userPlanScaleSpinner, new GridBagConstraints( 1, 4, 1, 1, 0, 0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, lastComponentInsets, 0, 0)); // 3D view component topPanel.add(this.view3DPrintedCheckBox, new GridBagConstraints( 0, 5, 2, 1, 0, 0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); add(topPanel, new GridBagConstraints( 0, 0, 2, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 5, 0), 0, 0)); // Second row add(new JSeparator(), new GridBagConstraints( 0, 1, 2, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 5, 0), -10, 0)); // Third row add(this.headerFormatLabel, new GridBagConstraints( 0, 2, 1, 1, 0, 0, labelAlignment, GridBagConstraints.NONE, new Insets(0, 0, 5, 5), 0, 0)); add(this.headerFormatTextField, new GridBagConstraints( 1, 2, 1, 1, 0, 0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, lastComponentInsets, 0, 0)); // Forth row add(this.footerFormatLabel, new GridBagConstraints( 0, 3, 1, 1, 0, 0, labelAlignment, GridBagConstraints.NONE, new Insets(0, 0, 5, 5), 0, 0)); add(this.footerFormatTextField, new GridBagConstraints( 1, 3, 1, 1, 0, 0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, lastComponentInsets, 0, 0)); // Last row add(this.variablesLabel, new GridBagConstraints( 0, 4, 1, 1, 0, 0, labelAlignment, GridBagConstraints.NONE, new Insets(0, 0, 0, 5), 0, 0)); add(this.variableButtonsToolBar, new GridBagConstraints( 1, 4, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); } /** * Displays this panel in a modal dialog box. */ public void displayView(View parentView) { if (SwingTools.showConfirmDialog((JComponent)parentView, this, this.dialogTitle, this.pageFormatButton) == JOptionPane.OK_OPTION && this.controller != null) { this.controller.modifyPageSetup(); } } }