/* * IzPack - Copyright 2001-2008 Julien Ponge, All Rights Reserved. * * http://izpack.org/ * http://izpack.codehaus.org/ * * Copyright 2002 Marcus Wolschon * Copyright 2002 Jan Blok * Copyright 2004 Klaus Bartz * Copyright 2007 Dennis Reil * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.izforge.izpack.panels; import com.izforge.izpack.LocaleDatabase; import com.izforge.izpack.Pack; import com.izforge.izpack.gui.LabelFactory; import com.izforge.izpack.installer.*; import com.izforge.izpack.util.Debug; import com.izforge.izpack.util.IoHelper; import com.izforge.izpack.util.VariableSubstitutor; import com.izforge.izpack.adaptator.IXMLElement; import javax.swing.*; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.plaf.metal.MetalLookAndFeel; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableCellRenderer; import java.awt.*; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.io.File; import java.io.InputStream; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; /** * The base class for Packs panels. It brings the common member and methods of the different packs * panels together. This class handles the common logic of pack selection. The derived class should * be create the layout and other specific actions. There are some helper methods to simplify layout * creation in the derived class. * * @author Julien Ponge * @author Klaus Bartz * @author Dennis Reil */ public abstract class PacksPanelBase extends IzPanel implements PacksPanelInterface, ListSelectionListener { // Common used Swing fields /** * The free space label. */ protected JLabel freeSpaceLabel; /** * The space label. */ protected JLabel spaceLabel; /** * The tip label. */ protected JTextArea descriptionArea; /** * The dependencies label. */ protected JTextArea dependencyArea; /** * The packs table. */ protected JTable packsTable; /** * The packs model. */ protected PacksModel packsModel; /** * The tablescroll. */ protected JScrollPane tableScroller; // Non-GUI fields /** * Map that connects names with pack objects */ private Map<String, Pack> names; /** * The bytes of the current pack. */ protected long bytes = 0; /** * The free bytes of the current selected disk. */ protected long freeBytes = 0; /** * Are there dependencies in the packs */ protected boolean dependenciesExist = false; /** * The packs locale database. */ private LocaleDatabase langpack = null; /** * The name of the XML file that specifies the panel langpack */ private static final String LANG_FILE_NAME = "packsLang.xml"; private Debugger debugger; /** * The constructor. * * @param parent The parent window. * @param idata The installation data. */ public PacksPanelBase(InstallerFrame parent, InstallData idata) { super(parent, idata); // Load langpack. try { this.langpack = parent.langpack; InputStream inputStream = ResourceManager.getInstance().getInputStream(LANG_FILE_NAME); this.langpack.add(inputStream); this.debugger = parent.getDebugger(); } catch (Throwable exception) { Debug.trace(exception); } // init the map computePacks(idata.availablePacks); createNormalLayout(); } /** * The Implementation of this method should create the layout for the current class. */ abstract protected void createNormalLayout(); /* * (non-Javadoc) * * @see com.izforge.izpack.panels.PacksPanelInterface#getLangpack() */ public LocaleDatabase getLangpack() { return (langpack); } /* * (non-Javadoc) * * @see com.izforge.izpack.panels.PacksPanelInterface#getBytes() */ public long getBytes() { return bytes; } /* * (non-Javadoc) * * @see com.izforge.izpack.panels.PacksPanelInterface#setBytes(int) */ public void setBytes(long bytes) { this.bytes = bytes; } /* * (non-Javadoc) * * @see com.izforge.izpack.panels.PacksPanelInterface#showSpaceRequired() */ public void showSpaceRequired() { if (spaceLabel != null) { spaceLabel.setText(Pack.toByteUnitsString(bytes)); } } /* * (non-Javadoc) * * @see com.izforge.izpack.panels.PacksPanelInterface#showFreeSpace() */ public void showFreeSpace() { if (IoHelper.supported("getFreeSpace") && freeSpaceLabel != null) { String msg; freeBytes = IoHelper.getFreeSpace(IoHelper.existingParent( new File(idata.getInstallPath())).getAbsolutePath()); if (freeBytes < 0) { msg = parent.langpack.getString("PacksPanel.notAscertainable"); } else { msg = Pack.toByteUnitsString(freeBytes); } freeSpaceLabel.setText(msg); } } /** * Indicates wether the panel has been validated or not. * * @return true if the needed space is less than the free space, else false */ public boolean isValidated() { if (IoHelper.supported("getFreeSpace") && freeBytes >= 0 && freeBytes <= bytes) { JOptionPane.showMessageDialog(this, parent.langpack .getString("PacksPanel.notEnoughSpace"), parent.langpack .getString("installer.error"), JOptionPane.ERROR_MESSAGE); return (false); } for (Pack pack : idata.availablePacks) { for (String validator : pack.getValidators()) { /* * This will call * public static boolean validate(AbstractUIHandler handler, * InstallData idata, String packsId, boolean isSelected) * from the validator class */ PackValidator validatorInst; try { validatorInst = (PackValidator) Class.forName(validator).newInstance(); } catch (Exception e) { e.printStackTrace(); System.err.println("Error: Validator class "+validator +" for pack "+pack.name+" not available."); continue; } try { if (validatorInst.validate(this, idata, pack.id, (idata.selectedPacks.indexOf(pack) > -1))) continue; else return false; } catch (Exception e) { e.printStackTrace(); System.err.println("Error: Exception in "+validator+".validate(" +(idata.selectedPacks.indexOf(pack) > -1)+") for pack "+pack.name); continue; } } } return (true); } /** * Asks to make the XML panel data. * * @param panelRoot The XML tree to write the data in. */ public void makeXMLData(IXMLElement panelRoot) { new ImgPacksPanelAutomationHelper().makeXMLData(idata, panelRoot); } /* * (non-Javadoc) * * @see javax.swing.event.ListSelectionListener#valueChanged(javax.swing.event.ListSelectionEvent) */ public void valueChanged(ListSelectionEvent e) { int selectedRow = packsTable.getSelectedRow(); // Operations for the description if ((descriptionArea != null) && (selectedRow != -1)) { Pack pack = (Pack) idata.availablePacks.get(selectedRow); String desc = ""; String key = pack.id + ".description"; if (langpack != null && pack.id != null && !"".equals(pack.id)) { desc = langpack.getString(key); } if ("".equals(desc) || key.equals(desc)) { desc = pack.description; } VariableSubstitutor vs = new VariableSubstitutor(idata.getVariables()); desc = vs.substitute(desc, null); descriptionArea.setText(desc); } // Operation for the dependency listing if ((dependencyArea != null) && (selectedRow != -1)) { Pack pack = (Pack) idata.availablePacks.get(selectedRow); List<String> dep = pack.dependencies; String list = ""; if (dep != null) { list += (langpack == null) ? "Dependencies: " : langpack .getString("PacksPanel.dependencies"); } for (int j = 0; dep != null && j < dep.size(); j++) { String name = dep.get(j); list += getI18NPackName(names.get(name)); if (j != dep.size() - 1) { list += ", "; } } // add the list of the packs to be excluded String excludeslist = (langpack == null) ? "Excludes: " : langpack .getString("PacksPanel.excludes"); int numexcludes = 0; if (pack.excludeGroup != null) { for (int q = 0; q < idata.availablePacks.size(); q++) { Pack otherpack = (Pack) idata.availablePacks.get(q); String exgroup = otherpack.excludeGroup; if (exgroup != null) { if (q != selectedRow && pack.excludeGroup.equals(exgroup)) { excludeslist += getI18NPackName(otherpack) + ", "; numexcludes++; } } } } // concatenate if (dep != null) { excludeslist = " " + excludeslist; } if (numexcludes > 0) { list += excludeslist; } if (list.endsWith(", ")) { list = list.substring(0, list.length() - 2); } // and checkbox the result dependencyArea.setText(list); } } /** * This method tries to resolve the localized name of the given pack. If this is not possible, * the name given in the installation description file in ELEMENT <pack> will be used. * * @param pack for which the name should be resolved * @return localized name of the pack */ private String getI18NPackName(Pack pack) { // Internationalization code String packName = pack.name; String key = pack.id; if (langpack != null && pack.id != null && !"".equals(pack.id)) { packName = langpack.getString(key); } if ("".equals(packName) || key == null || key.equals(packName)) { packName = pack.name; } return (packName); } /** * Layout helper method:<br> * Creates an label with a message given by msgId and an icon given by the iconId. If layout and * constraints are not null, the label will be added to layout with the given constraints. The * label will be added to this object. * * @param msgId identifier for the IzPack langpack * @param iconId identifier for the IzPack icons * @param layout layout to be used * @param constraints constraints to be used * @return the created label */ protected JLabel createLabel(String msgId, String iconId, GridBagLayout layout, GridBagConstraints constraints) { JLabel label = LabelFactory.create(parent.langpack.getString(msgId), parent.icons .getImageIcon(iconId), TRAILING); if (layout != null && constraints != null) { layout.addLayoutComponent(label, constraints); } add(label); return (label); } /** * Creates a panel containing a anonymous label on the left with the message for the given msgId * and a label on the right side with initial no text. The right label will be returned. If * layout and constraints are not null, the label will be added to layout with the given * constraints. The panel will be added to this object. * * @param msgId identifier for the IzPack langpack * @param layout layout to be used * @param constraints constraints to be used * @return the created (right) label */ protected JLabel createPanelWithLabel(String msgId, GridBagLayout layout, GridBagConstraints constraints) { JPanel panel = new JPanel(); JLabel label = new JLabel(); panel.setAlignmentX(LEFT_ALIGNMENT); panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); panel.add(LabelFactory.create(parent.langpack.getString(msgId))); panel.add(Box.createHorizontalGlue()); panel.add(label); if (layout != null && constraints != null) { layout.addLayoutComponent(panel, constraints); } add(panel); return (label); } /** * Creates a text area with standard settings and the title given by the msgId. If scroller is * not null, the create text area will be added to the scroller and the scroller to this object, * else the text area will be added directly to this object. If layout and constraints are not * null, the text area or scroller will be added to layout with the given constraints. The text * area will be returned. * * @param msgId identifier for the IzPack langpack * @param scroller the scroller to be used * @param layout layout to be used * @param constraints constraints to be used * @return the created text area */ protected JTextArea createTextArea(String msgId, JScrollPane scroller, GridBagLayout layout, GridBagConstraints constraints) { JTextArea area = new JTextArea(); // area.setMargin(new Insets(2, 2, 2, 2)); area.setAlignmentX(LEFT_ALIGNMENT); area.setCaretPosition(0); area.setEditable(false); area.setEditable(false); area.setOpaque(false); area.setLineWrap(true); area.setWrapStyleWord(true); area.setBorder(BorderFactory.createTitledBorder(parent.langpack.getString(msgId))); area.setFont(getControlTextFont()); if (layout != null && constraints != null) { if (scroller != null) { layout.addLayoutComponent(scroller, constraints); } else { layout.addLayoutComponent(area, constraints); } } if (scroller != null) { scroller.setViewportView(area); add(scroller); } else { add(area); } return (area); } /** * Creates the table for the packs. All parameters are required. The table will be returned. * * @param width of the table * @param scroller the scroller to be used * @param layout layout to be used * @param constraints constraints to be used * @return the created table */ protected JTable createPacksTable(int width, JScrollPane scroller, GridBagLayout layout, GridBagConstraints constraints) { JTable table = new JTable(); table.setBorder(BorderFactory.createEmptyBorder(0, 2, 0, 2)); table.setIntercellSpacing(new Dimension(0, 0)); table.setBackground(Color.white); table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); table.getSelectionModel().addListSelectionListener(this); table.setShowGrid(false); scroller.setViewportView(table); scroller.setAlignmentX(LEFT_ALIGNMENT); scroller.getViewport().setBackground(Color.white); scroller.setPreferredSize(new Dimension(width, (idata.guiPrefs.height / 3 + 30))); if (layout != null && constraints != null) { layout.addLayoutComponent(scroller, constraints); } add(scroller); return (table); } /** * Computes pack related data like the names or the dependencies state. * * @param packs The list of packs. */ private void computePacks(List packs) { names = new HashMap<String, Pack>(); dependenciesExist = false; for (Object pack1 : packs) { Pack pack = (Pack) pack1; names.put(pack.name, pack); if (pack.dependencies != null || pack.excludeGroup != null) { dependenciesExist = true; } } } /** * Called when the panel becomes active. If a derived class implements this method also, it is * recomanded to call this method with the super operator first. */ public void panelActivate() { try { packsModel = new PacksModel(this, idata, this.parent.getRules()) { @Override public boolean isCellEditable(int rowIndex, int columnIndex) { return false; } }; packsTable.setModel(packsModel); CheckBoxRenderer packSelectedRenderer = new CheckBoxRenderer(); packsTable.getColumnModel().getColumn(0).setCellRenderer(packSelectedRenderer); packsTable.getColumnModel().getColumn(0).setMaxWidth(40); packsTable.getColumnModel().getColumn(1).setCellRenderer(new PacksPanelTableCellRenderer()); PacksPanelTableCellRenderer packTextColumnRenderer = new PacksPanelTableCellRenderer(); packTextColumnRenderer.setHorizontalAlignment(RIGHT); packsTable.getColumnModel().getColumn(2).setCellRenderer(packTextColumnRenderer); packsTable.getColumnModel().getColumn(2).setMaxWidth(100); for (MouseListener mouseListener : packsTable.getMouseListeners()) { packsTable.removeMouseListener(mouseListener); } packsTable.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent event) { int row = packsTable.rowAtPoint(event.getPoint()); int col = packsTable.columnAtPoint(event.getPoint()); if (col == 0) { Integer checked = (Integer) packsModel.getValueAt(row, 0); checked = (checked == 0) ? 1 : 0; packsModel.setValueAt(checked, row, 0); packsTable.repaint(); } } }); // remove header,so we don't need more strings tableScroller.remove(packsTable.getTableHeader()); tableScroller.setColumnHeaderView(null); tableScroller.setColumnHeader(null); // set the JCheckBoxes to the currently selected panels. The // selection might have changed in another panel java.util.Iterator iter = idata.availablePacks.iterator(); bytes = 0; while (iter.hasNext()) { Pack p = (Pack) iter.next(); if (p.required) { bytes += p.nbytes; continue; } if (idata.selectedPacks.contains(p)) { bytes += p.nbytes; } } } catch (Exception e) { e.printStackTrace(); } showSpaceRequired(); showFreeSpace(); packsTable.setRowSelectionInterval(0, 0); } /* * (non-Javadoc) * * @see com.izforge.izpack.installer.IzPanel#getSummaryBody() */ public String getSummaryBody() { StringBuffer retval = new StringBuffer(256); Iterator iter = idata.selectedPacks.iterator(); boolean first = true; while (iter.hasNext()) { if (!first) { retval.append("<br>"); } first = false; Pack pack = (Pack) iter.next(); if (langpack != null && pack.id != null && !"".equals(pack.id)) { retval.append(langpack.getString(pack.id)); } else { retval.append(pack.name); } } if (packsModel.isModifyinstallation()) { Map installedpacks = packsModel.getInstalledpacks(); iter = installedpacks.keySet().iterator(); retval.append("<br><b>"); retval.append(langpack.getString("PacksPanel.installedpacks.summarycaption")); retval.append("</b>"); retval.append("<br>"); while (iter.hasNext()) { Pack pack = (Pack) installedpacks.get(iter.next()); if (langpack != null && pack.id != null && !"".equals(pack.id)) { retval.append(langpack.getString(pack.id)); } else { retval.append(pack.name); } retval.append("<br>"); } } return (retval.toString()); } static class CheckBoxRenderer implements TableCellRenderer { JCheckBox checkbox = new JCheckBox(); CheckBoxRenderer() { if (com.izforge.izpack.util.OsVersion.IS_UNIX && !com.izforge.izpack.util.OsVersion.IS_OSX) { checkbox.setIcon(new LFIndependentIcon()); checkbox.setDisabledIcon(new LFIndependentIcon()); checkbox.setSelectedIcon(new LFIndependentIcon()); checkbox.setDisabledSelectedIcon(new LFIndependentIcon()); } checkbox.setHorizontalAlignment(CENTER); } public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { if (isSelected) { checkbox.setForeground(table.getSelectionForeground()); checkbox.setBackground(table.getSelectionBackground()); } else { checkbox.setForeground(table.getForeground()); checkbox.setBackground(table.getBackground()); } int state = (Integer) value; if (state == -2) { // condition not fulfilled checkbox.setForeground(Color.GRAY); } if (state == -3) { checkbox.setForeground(Color.RED); checkbox.setSelected(true); } checkbox.setEnabled(state >= 0); checkbox.setSelected((value != null && Math.abs(state) == 1)); return checkbox; } } public static class LFIndependentIcon implements Icon { ButtonModel buttonModel = null; protected int getControlSize() { return 13; } public void paintIcon(Component c, Graphics g, int x, int y) { ButtonModel model = ((JCheckBox) c).getModel(); buttonModel = model; int controlSize = getControlSize(); if (model.isPressed() && model.isArmed()) { g.setColor(MetalLookAndFeel.getControlShadow()); if (model.isEnabled()) { g.setColor(Color.green); } else { g.setColor(Color.gray); } g.fillRect(x, y, controlSize - 1, controlSize - 1); drawPressedBorder(g, x, y, controlSize, controlSize, model); } else { drawBorder(g, x, y, controlSize, controlSize, model); } g.setColor(Color.green); if (model.isSelected()) { drawCheck(g, x, y); } } private void drawBorder(Graphics g, int x, int y, int w, int h, ButtonModel model) { g.translate(x, y); // outer frame rectangle g.setColor(MetalLookAndFeel.getControlDarkShadow()); if (!model.isEnabled()) { g.setColor(new Color(0.4f, 0.4f, 0.4f)); } g.drawRect(0, 0, w - 2, h - 2); // middle frame g.setColor(MetalLookAndFeel.getControlHighlight()); if (!model.isEnabled()) { g.setColor(new Color(0.6f, 0.6f, 0.6f)); } g.drawRect(1, 1, w - 2, h - 2); // background if (model.isEnabled()) { g.setColor(Color.white); } else { g.setColor(new Color(0.8f, 0.8f, 0.8f)); } g.fillRect(2, 2, w - 3, h - 3); //some extra lines for FX g.setColor(MetalLookAndFeel.getControl()); g.drawLine(0, h - 1, 1, h - 2); g.drawLine(w - 1, 0, w - 2, 1); g.translate(-x, -y); } private void drawPressedBorder(Graphics g, int x, int y, int w, int h, ButtonModel model) { g.translate(x, y); drawBorder(g, 0, 0, w, h, model); g.setColor(MetalLookAndFeel.getControlShadow()); g.drawLine(1, 1, 1, h - 2); g.drawLine(1, 1, w - 2, 1); g.drawLine(2, 2, 2, h - 3); g.drawLine(2, 2, w - 3, 2); g.translate(-x, -y); } protected void drawCheck(Graphics g, int x, int y) { int controlSize = getControlSize(); if (buttonModel != null) { if (buttonModel.isEnabled()) { g.setColor(new Color(0.0f, 0.6f, 0.0f)); } else { g.setColor(new Color(0.1f, 0.1f, 0.1f)); } } g.drawLine(x + (controlSize - 4), y + 2, x + (controlSize - 4) - 4, y + 2 + 4); g.drawLine(x + (controlSize - 4), y + 3, x + (controlSize - 4) - 4, y + 3 + 4); g.drawLine(x + (controlSize - 4), y + 4, x + (controlSize - 4) - 4, y + 4 + 4); g.drawLine(x + (controlSize - 4) - 4, y + 2 + 4, x + (controlSize - 4) - 4 - 2, y + 2 + 4 - 2); g.drawLine(x + (controlSize - 4) - 4, y + 3 + 4, x + (controlSize - 4) - 4 - 2, y + 3 + 4 - 2); g.drawLine(x + (controlSize - 4) - 4, y + 4 + 4, x + (controlSize - 4) - 4 - 2, y + 4 + 4 - 2); } public int getIconWidth() { return getControlSize(); } public int getIconHeight() { return getControlSize(); } } static class PacksPanelTableCellRenderer extends DefaultTableCellRenderer { /** * Required (serializable) */ private static final long serialVersionUID = -9089892183236584242L; public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { Component renderer = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); int state = (Integer) table.getModel().getValueAt(row, 0); if (state == -2) { // condition not fulfilled renderer.setForeground(Color.GRAY); if (isSelected) { renderer.setBackground(table.getSelectionBackground()); } else { renderer.setBackground(table.getBackground()); } } else { if (isSelected) { renderer.setForeground(table.getSelectionForeground()); renderer.setBackground(table.getSelectionBackground()); } else { renderer.setForeground(table.getForeground()); renderer.setBackground(table.getBackground()); } } return renderer; } } public Debugger getDebugger() { return this.debugger; } }