[izpack-changes] r1853 - izpack-src/trunk/src/lib/com/izforge/izpack/panels
noreply at berlios.de
noreply at berlios.de
Tue May 29 22:35:01 CEST 2007
Author: vralev
Date: 2007-05-29 22:34:58 +0200 (Tue, 29 May 2007)
New Revision: 1853
Added:
izpack-src/trunk/src/lib/com/izforge/izpack/panels/TreePacksPanel.java
Log:
TreePacksPanel - hierarchical pack selection panel
Added: izpack-src/trunk/src/lib/com/izforge/izpack/panels/TreePacksPanel.java
===================================================================
--- izpack-src/trunk/src/lib/com/izforge/izpack/panels/TreePacksPanel.java 2007-05-29 20:33:54 UTC (rev 1852)
+++ izpack-src/trunk/src/lib/com/izforge/izpack/panels/TreePacksPanel.java 2007-05-29 20:34:58 UTC (rev 1853)
@@ -0,0 +1,1277 @@
+package com.izforge.izpack.panels;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.GridLayout;
+import java.awt.event.ItemEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.io.File;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.EventListener;
+import java.util.EventObject;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+
+import javax.swing.AbstractCellEditor;
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.ButtonModel;
+import javax.swing.Icon;
+import javax.swing.JCheckBox;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+import javax.swing.JTree;
+import javax.swing.UIManager;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.plaf.metal.MetalLookAndFeel;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.TreeCellEditor;
+import javax.swing.tree.TreeCellRenderer;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreeNode;
+import javax.swing.tree.TreePath;
+
+import net.n3.nanoxml.XMLElement;
+
+import com.izforge.izpack.LocaleDatabase;
+import com.izforge.izpack.Pack;
+import com.izforge.izpack.gui.LabelFactory;
+import com.izforge.izpack.gui.TwoColumnLayout;
+import com.izforge.izpack.installer.InstallData;
+import com.izforge.izpack.installer.InstallerFrame;
+import com.izforge.izpack.installer.IzPanel;
+import com.izforge.izpack.installer.ResourceManager;
+import com.izforge.izpack.util.Debug;
+import com.izforge.izpack.util.IoHelper;
+import com.izforge.izpack.util.VariableSubstitutor;
+
+public class TreePacksPanel extends IzPanel implements PacksPanelInterface
+{
+ // 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 tree.
+ */
+ protected JTree packsTree;
+
+ /**
+ * The packs model.
+ */
+ protected PacksModel packsModel;
+
+ /**
+ * The tablescroll.
+ */
+ protected JScrollPane tableScroller;
+
+ // Non-GUI fields
+ /**
+ * Map that connects names with pack objects
+ */
+ private Map names;
+
+ /**
+ * The bytes of the current pack.
+ */
+ protected int 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 HashMap idToPack;
+ private HashMap treeData;
+
+ private HashMap idToCheckBoxNode = new HashMap();
+ private boolean created = false;
+
+ /**
+ * The constructor.
+ *
+ * @param parent The parent window.
+ * @param idata The installation data.
+ */
+ public TreePacksPanel(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);
+ }
+ catch (Throwable exception)
+ {
+ Debug.trace(exception);
+ }
+
+ // init the map
+ computePacks(idata.availablePacks);
+
+ }
+
+ /**
+ * The Implementation of this method should create the layout for the current class.
+ */
+
+ protected void createNormalLayout()
+ {
+ this.removeAll();
+ setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+ createLabel("PacksPanel.info", "preferences", null, null);
+ add(Box.createRigidArea(new Dimension(0, 3)));
+ createLabel("PacksPanel.tip", "tip", null, null);
+ add(Box.createRigidArea(new Dimension(0, 5)));
+ tableScroller = new JScrollPane();
+ packsTree = createPacksTree(300, tableScroller, null, null);
+ if (dependenciesExist)
+ dependencyArea = createTextArea("PacksPanel.dependencyList", null, null, null);
+ descriptionArea = createTextArea("PacksPanel.description", null, null, null);
+ spaceLabel = createPanelWithLabel("PacksPanel.space", null, null);
+ if (IoHelper.supported("getFreeSpace"))
+ {
+ add(Box.createRigidArea(new Dimension(0, 3)));
+ freeSpaceLabel = createPanelWithLabel("PacksPanel.freespace", null, null);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.izforge.izpack.panels.PacksPanelInterface#getLangpack()
+ */
+ public LocaleDatabase getLangpack()
+ {
+ return (langpack);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.izforge.izpack.panels.PacksPanelInterface#getBytes()
+ */
+ public int getBytes()
+ {
+ return (bytes);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.izforge.izpack.panels.PacksPanelInterface#setBytes(int)
+ */
+ public void setBytes(int 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 = null;
+ 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()
+ {
+ refreshPacksToInstall();
+ 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);
+ }
+ return (true);
+ }
+
+ /**
+ * Asks to make the XML panel data.
+ *
+ * @param panelRoot The XML tree to write the data in.
+ */
+ public void makeXMLData(XMLElement panelRoot)
+ {
+ new ImgPacksPanelAutomationHelper().makeXMLData(idata, panelRoot);
+ }
+
+
+ /**
+ * 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);
+ }
+
+ public String getI18NPackName(String packId)
+ {
+ Pack pack = (Pack) idToPack.get(packId);
+ if(pack == null) return packId;
+ // 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();
+ if (label == null) 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);
+ }
+
+ private void refreshPacksToInstall()
+ {
+ idata.selectedPacks.clear();
+ CheckBoxNode cbn = (CheckBoxNode) getTree().getModel().getRoot();
+ Enumeration e = cbn.depthFirstEnumeration();
+ while(e.hasMoreElements())
+ {
+ CheckBoxNode c = (CheckBoxNode) e.nextElement();
+ if(c.isSelected() || c.isPartial())
+ {
+ idata.selectedPacks.add(c.getPack());
+ }
+ }
+ }
+
+ /**
+ * 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);
+
+ }
+
+ /**
+ * FIXME Creates the JTree component and calls all initialization tasks
+ *
+ * @param width
+ * @param scroller
+ * @param layout
+ * @param constraints
+ * @return
+ */
+ protected JTree createPacksTree(int width, JScrollPane scroller, GridBagLayout layout,
+ GridBagConstraints constraints)
+ {
+ JTree tree = new JTree((CheckBoxNode)populateTreePacks(null));
+ packsTree = tree;
+ tree.setCellRenderer(new CheckBoxNodeRenderer(this));
+ tree.setEditable(false);
+ tree.setShowsRootHandles(true);
+ tree.setRootVisible(false);
+ tree.addMouseListener(new CheckTreeController(this));
+ tree.setBorder(BorderFactory.createEmptyBorder(0, 2, 0, 2));
+ tree.setBackground(Color.white);
+ tree.setToggleClickCount(0);
+ //tree.setRowHeight(0);
+
+ //table.getSelectionModel().addTreeSelectionListener(this);
+ scroller.setViewportView(tree);
+ 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 (tree);
+ }
+
+ /**
+ * Computes pack related data like the names or the dependencies state.
+ *
+ * @param packs
+ */
+ private void computePacks(List packs)
+ {
+ names = new HashMap();
+ dependenciesExist = false;
+ for (int i = 0; i < packs.size(); i++)
+ {
+ Pack pack = (Pack) packs.get(i);
+ names.put(pack.name, pack);
+ if (pack.dependencies != null || pack.excludeGroup != null) dependenciesExist = true;
+ }
+ }
+
+ /**
+ * Refresh tree data from the PacksModel. This functions serves as a bridge
+ * between the flat PacksModel and the tree data model.
+ *
+ */
+ public void fromModel()
+ {
+ TreeModel model = this.packsTree.getModel();
+ CheckBoxNode root = (CheckBoxNode)model.getRoot();
+ updateModel(root);
+ }
+
+ /**
+ * Helper function for fromModel() - runs the recursion
+ *
+ * @param rnode
+ */
+ private void updateModel(CheckBoxNode rnode)
+ {
+ int rowIndex = idata.availablePacks.indexOf(rnode.getPack());
+ if(rowIndex > 0)
+ {
+ Integer state = (Integer) packsModel.getValueAt(rowIndex, 0);
+ if( (state.intValue() == -2) && rnode.getChildCount() > 0)
+ {
+ boolean dirty = false;
+ Enumeration toBeDeselected = rnode.depthFirstEnumeration();
+ while(toBeDeselected.hasMoreElements())
+ {
+ CheckBoxNode cbn = (CheckBoxNode) toBeDeselected.nextElement();
+ boolean chDirty = cbn.isSelected() || cbn.isPartial() || cbn.isEnabled();
+ dirty = dirty || chDirty;
+ if(chDirty)
+ {
+ cbn.setPartial(false);
+ cbn.setSelected(false);
+ cbn.setEnabled(false);
+ setModelValue(cbn);
+ }
+ }
+ if(dirty) fromModel();
+ return;
+ }
+ }
+
+ Enumeration e = rnode.children();
+ while(e.hasMoreElements())
+ {
+ Object next = e.nextElement();
+ CheckBoxNode cbnode = (CheckBoxNode) next;
+ String nodeText = cbnode.getId();
+ Object nodePack = idToPack.get(nodeText);
+ if(!cbnode.isPartial())
+ {
+ int childRowIndex = idata.availablePacks.indexOf(nodePack);
+ if(childRowIndex > 0)
+ {
+ Integer state = (Integer) packsModel.getValueAt(childRowIndex, 0);
+ cbnode.setEnabled(state.intValue() >= 0);
+ cbnode.setSelected(Math.abs(state.intValue()) == 1);
+ }
+ }
+ updateModel(cbnode);
+ }
+ }
+
+ /**
+ * Updates a value for pack in PacksModel with data from a checkbox node
+ *
+ * @param id pack id
+ * @param cbnode This is the checkbox node which contains model values
+ */
+ public void setModelValue(CheckBoxNode cbnode)
+ {
+ String id = cbnode.getId();
+ Object nodePack = idToPack.get(id);
+ int value = 0;
+ if(cbnode.isEnabled() && cbnode.isSelected()) value = 1;
+ if(!cbnode.isEnabled() && cbnode.isSelected()) value = -1;
+ if(!cbnode.isEnabled() && !cbnode.isSelected()) value = -2;
+ int rowIndex = idata.availablePacks.indexOf(nodePack);
+ if(rowIndex > 0)
+ {
+ Integer newValue = new Integer(value);
+ Integer modelValue = (Integer) packsModel.getValueAt(rowIndex, 0);
+ if(!newValue.equals(modelValue))
+ packsModel.setValueAt(newValue, rowIndex, 0);
+ }
+ }
+
+ /**
+ * Initialize tree model sructures
+ *
+ */
+ private void createTreeData()
+ {
+ treeData = new HashMap();
+ idToPack = new HashMap();
+
+ java.util.Iterator iter = idata.availablePacks.iterator();
+ while (iter.hasNext())
+ {
+ Pack p = (Pack) iter.next();
+ idToPack.put(p.id, p);
+ if(p.parent != null)
+ {
+ ArrayList kids = null;
+ if(treeData.containsKey(p.parent))
+ kids = (ArrayList)treeData.get(p.parent);
+ else
+ {
+ kids = new ArrayList();
+ }
+ kids.add(p.id);
+ treeData.put(p.parent, kids);
+ }
+ }
+ }
+
+ /**
+ * Shows and updates the description text in the panel
+ *
+ * @param id
+ */
+ public void setDescription(String id)
+ {
+ VariableSubstitutor vs = new VariableSubstitutor(idata.getVariables());
+ if (descriptionArea != null)
+ {
+ Pack pack = (Pack) idToPack.get(id);
+ 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;
+ }
+ desc = vs.substitute(desc, null);
+ descriptionArea.setText(desc);
+ }
+ }
+
+ /**
+ * Shows and updates the dependencies text in the panel
+ *
+ * @param id
+ */
+ public void setDependencies(String id)
+ {
+ if (descriptionArea != null)
+ {
+ Pack pack = (Pack) idToPack.get(id);
+ List 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 = (String) dep.get(j);
+ list += getI18NPackName((Pack) 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;
+ int i = idata.availablePacks.indexOf(pack);
+ 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 != i && 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 display the result
+ dependencyArea.setText(list);
+ }
+ }
+
+ /**
+ * Gives a CheckBoxNode instance from the id
+ *
+ * @param id
+ * @return
+ */
+ public CheckBoxNode getCbnById(String id)
+ {
+ return (CheckBoxNode) this.idToCheckBoxNode.get(id);
+ }
+
+ /**
+ * Reads the available packs and creates the JTree structure based on
+ * the parent definitions.
+ *
+ * @param parent
+ * @return
+ */
+ private Object populateTreePacks(String parent)
+ {
+ if(parent == null) // the root node
+ {
+ java.util.Iterator iter = idata.availablePacks.iterator();
+ ArrayList rootNodes = new ArrayList();
+ while (iter.hasNext())
+ {
+ Pack p = (Pack) iter.next();
+ if(p.parent == null)
+ {
+ rootNodes.add(populateTreePacks(p.id));
+ }
+ }
+ TreeNode nv = new CheckBoxNode("Root", "Root", rootNodes.toArray(), true);
+ return nv;
+ }
+ else
+ {
+ ArrayList links = new ArrayList();
+ Object kidsObject = treeData.get(parent);
+ Pack p = (Pack) idToPack.get(parent);
+ String translated = getI18NPackName(parent);
+
+ if(kidsObject != null)
+ {
+ ArrayList kids = (ArrayList) kidsObject;
+ for(int q=0; q<kids.size(); q++)
+ {
+ String kidId = (String) kids.get(q);
+ links.add(populateTreePacks(kidId));
+ }
+
+ CheckBoxNode cbn = new CheckBoxNode(parent, translated, links.toArray(), true);
+ idToCheckBoxNode.put(cbn.getId(), cbn);
+ cbn.setPack(p);
+ cbn.setTotalSize(p.nbytes);
+ return cbn;
+ }
+ else
+ {
+ CheckBoxNode cbn = new CheckBoxNode(parent, translated, true);
+ idToCheckBoxNode.put(cbn.getId(), cbn);
+ cbn.setPack(p);
+ cbn.setTotalSize(p.nbytes);
+ return cbn;
+ }
+ }
+ }
+
+ /**
+ * 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
+ {
+
+ // TODO the PacksModel could be patched such that isCellEditable
+ // allows returns false. In that case the PacksModel must not be
+ // adapted here.
+ packsModel = new PacksModel(this, idata, this.parent.getRules()) {
+ public boolean isCellEditable(int rowIndex, int columnIndex) { return false; }
+ };
+
+ // Init tree structures
+ createTreeData();
+
+ // Create panel GUI (and populate the TJtree)
+ createNormalLayout();
+
+ // Reload the data from the PacksModel into the tree in order the initial
+ // dependencies to be resolved and effective
+ fromModel();
+
+ // Init the pack sizes (individual and cumulative)
+ CheckBoxNode cbn = (CheckBoxNode) packsTree.getModel().getRoot();
+ CheckTreeController.initTotalSize(cbn, false);
+
+ // Ugly repaint because of a bug in tree.treeDidChange
+ packsTree.revalidate();
+ packsTree.repaint();
+
+ 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();
+ }
+
+ /*
+ * (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);
+ }
+ return (retval.toString());
+ }
+
+
+ public JTree getTree()
+ {
+ return packsTree;
+ }
+
+}
+
+/**
+ *
+ * The renderer model for individual checkbox nodes in a JTree. It renders the
+ * checkbox and a label for the pack size.
+ *
+ * @author <a href="vralev at redhat.com">Vladimir Ralev</a>
+ * @version $Revision: 1.1 $
+ */
+class CheckBoxNodeRenderer implements TreeCellRenderer {
+ private static final JPanel rendererPanel = new JPanel();
+ private static final JLabel packSizeLabel = new JLabel();
+ private static final JCheckBox checkbox = new JCheckBox();
+ private static final JCheckBox normalCheckBox = new JCheckBox();
+ private static final java.awt.Font normalFont = new JCheckBox().getFont();
+ private static final java.awt.Font boldFont = new java.awt.Font(normalFont.getFontName(),
+ java.awt.Font.BOLD,
+ normalFont.getSize());
+ private static final java.awt.Font plainFont = new java.awt.Font(normalFont.getFontName(),
+ java.awt.Font.PLAIN,
+ normalFont.getSize());
+ private static final Color annotationColor = new Color(0, 0, 120); // red
+ private static final Color changedColor = new Color(200, 0, 0);
+
+ private static Color selectionForeground, selectionBackground,
+ textForeground, textBackground;
+
+ TreePacksPanel treePacksPanel;
+ public CheckBoxNodeRenderer(TreePacksPanel t) {
+ selectionForeground = UIManager.getColor("Tree.selectionForeground");
+ selectionBackground = UIManager.getColor("Tree.selectionBackground");
+ textForeground = UIManager.getColor("Tree.textForeground");
+ textBackground = UIManager.getColor("Tree.textBackground");
+ treePacksPanel = t;
+
+ int treeWidth = t.getTree().getPreferredSize().width;
+ int height = checkbox.getPreferredSize().height;
+ int cellWidth = treeWidth - treeWidth / 4;
+
+ //Don't touch, it fixes various layout bugs in swing/awt
+ rendererPanel.setLayout(new java.awt.BorderLayout(0, 0));
+ rendererPanel.setBackground(textBackground);
+ rendererPanel.add(java.awt.BorderLayout.WEST, checkbox);
+
+ rendererPanel.setAlignmentX((float)0);
+ rendererPanel.setAlignmentY((float)0);
+ rendererPanel.add(java.awt.BorderLayout.EAST, packSizeLabel);
+
+ rendererPanel.setMinimumSize(new Dimension(cellWidth, height));
+ rendererPanel.setPreferredSize(new Dimension(cellWidth, height));
+ rendererPanel.setSize(new Dimension(cellWidth, height));
+
+ rendererPanel.setBorder(BorderFactory.createEmptyBorder(0,0,0,0));
+ }
+
+ public Component getTreeCellRendererComponent(JTree tree, Object value,
+ boolean selected, boolean expanded, boolean leaf, int row,
+ boolean hasFocus) {
+ treePacksPanel.fromModel();
+
+ if (selected) {
+ checkbox.setForeground(selectionForeground);
+ checkbox.setBackground(selectionBackground);
+ rendererPanel.setForeground(selectionForeground);
+ rendererPanel.setBackground(selectionBackground);
+ packSizeLabel.setBackground(selectionBackground);
+ } else {
+ checkbox.setForeground(textForeground);
+ checkbox.setBackground(textBackground);
+ rendererPanel.setForeground(textForeground);
+ rendererPanel.setBackground(textBackground);
+ packSizeLabel.setBackground(textBackground);
+ }
+
+ if ((value != null) && (value instanceof CheckBoxNode)) {
+ CheckBoxNode node = (CheckBoxNode) value;
+
+ if(node.isTotalSizeChanged())
+ packSizeLabel.setForeground(changedColor);
+ else
+ {
+ if(selected)
+ packSizeLabel.setForeground(selectionForeground);
+ else
+ {
+ packSizeLabel.setForeground(annotationColor);
+ }
+ }
+
+ checkbox.setText(node.getTranslatedText());
+
+ packSizeLabel.setText(Pack.toByteUnitsString(node.getTotalSize()));
+
+ if(node.isPartial())
+ checkbox.setSelected(false);
+ else
+ checkbox.setSelected(node.isSelected());
+
+ checkbox.setEnabled(node.isEnabled());
+ packSizeLabel.setEnabled(node.isEnabled());
+
+ if(node.getChildCount()>0)
+ {
+ checkbox.setFont(boldFont);
+ packSizeLabel.setFont(boldFont);
+ }
+ else
+ {
+ checkbox.setFont(normalFont);
+ packSizeLabel.setFont(plainFont);
+ }
+
+ if(node.isPartial())
+ {
+ checkbox.setIcon(new PartialIcon());
+ }
+ else
+ {
+ checkbox.setIcon(normalCheckBox.getIcon());
+ }
+ }
+ return rendererPanel;
+ }
+
+ public Component getCheckRenderer()
+ {
+ return rendererPanel;
+ }
+
+}
+
+/**
+ *
+ * The model structure for a JTree node.
+ *
+ * @author <a href="vralev at redhat.com">Vladimir Ralev</a>
+ * @version $Revision: 1.1 $
+ */
+class CheckBoxNode extends DefaultMutableTreeNode{
+
+ String id;
+ boolean selected;
+ boolean partial;
+ boolean enabled;
+ boolean totalSizeChanged;
+ String translatedText;
+ Pack pack;
+ long totalSize;
+
+ public CheckBoxNode(String id, String translated, boolean selected) {
+ this.id = id;
+ this.selected = selected;
+ this.translatedText = translated;
+ }
+
+ public CheckBoxNode(String id, String translated, Object elements[], boolean selected) {
+ this.id = id;
+ this.translatedText = translated;
+ for (int i = 0, n = elements.length; i < n; i++) {
+ CheckBoxNode tn = (CheckBoxNode) elements[i];
+ add(tn);
+ }
+ }
+
+ public boolean isLeaf()
+ {
+ return this.getChildCount() == 0;
+ }
+
+ public boolean isSelected() {
+ return selected;
+ }
+
+ public void setSelected(boolean newValue) {
+ selected = newValue;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String newValue) {
+ id = newValue;
+ }
+
+ public String toString() {
+ return getClass().getName() + "[" + id + "/" + selected + "]";
+ }
+
+ public boolean isPartial()
+ {
+ return partial;
+ }
+
+ public void setPartial(boolean partial)
+ {
+ this.partial = partial;
+ if(partial) setSelected(true);
+ }
+
+ public boolean isEnabled()
+ {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled)
+ {
+ this.enabled = enabled;
+ }
+
+ public String getTranslatedText()
+ {
+ return translatedText;
+ }
+
+ public void setTranslatedText(String translatedText)
+ {
+ this.translatedText = translatedText;
+ }
+
+ public Pack getPack()
+ {
+ return pack;
+ }
+
+ public void setPack(Pack pack)
+ {
+ this.pack = pack;
+ }
+
+ public long getTotalSize()
+ {
+ return totalSize;
+ }
+
+ public void setTotalSize(long totalSize)
+ {
+ this.totalSize = totalSize;
+ }
+
+ public boolean isTotalSizeChanged()
+ {
+ return totalSizeChanged;
+ }
+
+ public void setTotalSizeChanged(boolean totalSizeChanged)
+ {
+ this.totalSizeChanged = totalSizeChanged;
+ }
+}
+
+/**
+ *
+ * Special checkbox icon which shows partially selected nodes.
+ *
+ * @author <a href="vralev at redhat.com">Vladimir Ralev</a>
+ * @version $Revision: 1.1 $
+ */
+class PartialIcon implements Icon
+{
+ protected int getControlSize() { return 13; }
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ int controlSize = getControlSize();
+ g.setColor( MetalLookAndFeel.getControlShadow() );
+ g.fillRect( x, y, controlSize-1, controlSize-1);
+ drawBorder(g, x, y, controlSize, controlSize);
+
+ g.setColor( Color.green );
+ drawCheck(c,g,x,y);
+ }
+ private void drawBorder(Graphics g, int x, int y, int w, int h)
+ {
+ g.translate(x, y);
+
+ // outer frame rectangle
+ g.setColor(MetalLookAndFeel.getControlDarkShadow());
+ g.setColor(new Color(0.4f, 0.4f, 0.4f));
+ g.drawRect(0, 0, w-2, h-2);
+
+ // middle frame
+ g.setColor(MetalLookAndFeel.getControlHighlight());
+ g.setColor(new Color(0.6f, 0.6f, 0.6f));
+ g.drawRect(1, 1, w-2, h-2);
+
+ // background
+ g.setColor(new Color(0.99f, 0.99f, 0.99f));
+ 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);
+ }
+ protected void drawCheck(Component c, Graphics g, int x, int y)
+ {
+ int controlSize = getControlSize();
+ g.setColor(new Color(0.0f,0.7f,0.0f));
+
+ g.fillOval(x+controlSize/2-2, y+controlSize/2-2, 6, 6);
+ }
+ public int getIconWidth() {return getControlSize();}
+ public int getIconHeight() {return getControlSize();}
+}
+
+/**
+ *
+ * Controller class which handles the mouse clicks on checkbox nodes. Also
+ * contains utility methods to update the sizes and the states of the nodes.
+ *
+ * @author <a href="vralev at redhat.com">Vladimir Ralev</a>
+ * @version $Revision: 1.1 $
+ */
+class CheckTreeController extends MouseAdapter{
+ JTree tree;
+ TreePacksPanel treePacksPanel;
+ int checkWidth = new JCheckBox().getPreferredSize().width;
+
+ public CheckTreeController(TreePacksPanel p){
+ this.tree = p.getTree();
+ this.treePacksPanel = p;
+ }
+
+ public void mouseReleased(MouseEvent me){
+ TreePath path = tree.getPathForLocation(me.getX(), me.getY());
+ if(path==null)
+ return;
+ CheckBoxNode current = (CheckBoxNode) path.getLastPathComponent();
+ treePacksPanel.setDescription(current.getId());
+ treePacksPanel.setDependencies(current.getId());
+ if(me.getX()>tree.getPathBounds(path).x + checkWidth)
+ return;
+
+ current.setSelected(!current.isSelected());
+ current.setPartial(false);
+ treePacksPanel.setModelValue(current);
+ Enumeration e = current.depthFirstEnumeration();
+ while(e.hasMoreElements())
+ {
+ CheckBoxNode child = (CheckBoxNode) e.nextElement();
+ child.setSelected(current.isSelected());
+ if(!current.isSelected()) child.setPartial(false);
+ treePacksPanel.setModelValue(child);
+ }
+ CheckBoxNode root = (CheckBoxNode)current.getRoot();
+ treePacksPanel.fromModel();
+ updateParents(current);
+ List deps = current.getPack().revDependencies;
+ if(deps != null) for(int q=0; q<deps.size(); q++)
+ {
+ String id = (String)deps.get(q);
+ if (id == null) continue;
+ CheckBoxNode cbn = (CheckBoxNode) treePacksPanel.getCbnById(id);
+ updateParents(cbn);
+ }
+ initTotalSize(root, true);
+
+ // must override the bytes being computed at packsModel
+ treePacksPanel.setBytes((int)root.getTotalSize());
+ treePacksPanel.showSpaceRequired();
+ tree.treeDidChange();
+ }
+
+ private void updateParents(CheckBoxNode node)
+ {
+ CheckBoxNode parent = (CheckBoxNode) node.getParent();
+ if(parent != null && !parent.equals(parent.getRoot()))
+ {
+ Enumeration ne = parent.children();
+ boolean allSelected = true;
+ boolean allDeselected = true;
+ while(ne.hasMoreElements())
+ {
+ CheckBoxNode child = (CheckBoxNode) ne.nextElement();
+ if(child.isSelected()) allDeselected = false;
+ else allSelected = false;
+ }
+ if(parent.getChildCount()>0)
+ {
+ if(!allSelected && !allDeselected)
+ setPartialParent(parent);
+ else
+ parent.setPartial(false);
+ if(allSelected) parent.setSelected(true);
+ if(allDeselected) parent.setSelected(false);
+ treePacksPanel.setModelValue(parent);
+ if(allSelected || allDeselected) updateParents(parent);
+ }
+ //updateTotalSize(node);
+ }
+ }
+
+ public static void setPartialParent(CheckBoxNode node)
+ {
+ node.setPartial(true);
+ CheckBoxNode parent = (CheckBoxNode) node.getParent();
+ if(parent != null && !parent.equals(parent.getRoot())) setPartialParent(parent);
+ }
+
+ public static long initTotalSize(CheckBoxNode node, boolean markChanged)
+ {
+ if(node.isLeaf()) return node.getPack().nbytes;
+ Enumeration e = node.children();
+ Pack nodePack = node.getPack();
+ long bytes = 0;
+ if(nodePack != null)
+ bytes = nodePack.nbytes;
+ while(e.hasMoreElements())
+ {
+ CheckBoxNode c = (CheckBoxNode) e.nextElement();
+ long size = initTotalSize(c, markChanged);
+ if(c.isSelected() || c.isPartial())
+ {
+ bytes += size;
+ }
+ }
+ if(markChanged)
+ {
+ long old = node.getTotalSize();
+ if(old != bytes)
+ node.setTotalSizeChanged(true);
+ else
+ node.setTotalSizeChanged(false);
+ }
+ node.setTotalSize(bytes);
+ return bytes;
+ }
+}
\ No newline at end of file
More information about the izpack-changes
mailing list