0001: /*
0002: * IzPack - Copyright 2001-2008 Julien Ponge, All Rights Reserved.
0003: *
0004: * http://izpack.org/
0005: * http://izpack.codehaus.org/
0006: *
0007: * Copyright 2007 Vladimir Ralev
0008: *
0009: * Licensed under the Apache License, Version 2.0 (the "License");
0010: * you may not use this file except in compliance with the License.
0011: * You may obtain a copy of the License at
0012: *
0013: * http://www.apache.org/licenses/LICENSE-2.0
0014: *
0015: * Unless required by applicable law or agreed to in writing, software
0016: * distributed under the License is distributed on an "AS IS" BASIS,
0017: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0018: * See the License for the specific language governing permissions and
0019: * limitations under the License.
0020: */
0021:
0022: package com.izforge.izpack.panels;
0023:
0024: import java.awt.Color;
0025: import java.awt.Component;
0026: import java.awt.Dimension;
0027: import java.awt.Graphics;
0028: import java.awt.GridBagConstraints;
0029: import java.awt.GridBagLayout;
0030: import java.awt.event.MouseAdapter;
0031: import java.awt.event.MouseEvent;
0032: import java.io.File;
0033: import java.io.InputStream;
0034: import java.util.ArrayList;
0035: import java.util.Enumeration;
0036: import java.util.HashMap;
0037: import java.util.Iterator;
0038: import java.util.List;
0039: import java.util.Map;
0040:
0041: import javax.swing.BorderFactory;
0042: import javax.swing.Box;
0043: import javax.swing.BoxLayout;
0044: import javax.swing.Icon;
0045: import javax.swing.JCheckBox;
0046: import javax.swing.JLabel;
0047: import javax.swing.JOptionPane;
0048: import javax.swing.JPanel;
0049: import javax.swing.JScrollPane;
0050: import javax.swing.JTextArea;
0051: import javax.swing.JTree;
0052: import javax.swing.UIManager;
0053: import javax.swing.plaf.metal.MetalLookAndFeel;
0054: import javax.swing.tree.DefaultMutableTreeNode;
0055: import javax.swing.tree.TreeCellRenderer;
0056: import javax.swing.tree.TreeModel;
0057: import javax.swing.tree.TreeNode;
0058: import javax.swing.tree.TreePath;
0059:
0060: import net.n3.nanoxml.XMLElement;
0061:
0062: import com.izforge.izpack.LocaleDatabase;
0063: import com.izforge.izpack.Pack;
0064: import com.izforge.izpack.gui.LabelFactory;
0065: import com.izforge.izpack.installer.*;
0066: import com.izforge.izpack.util.Debug;
0067: import com.izforge.izpack.util.IoHelper;
0068: import com.izforge.izpack.util.VariableSubstitutor;
0069:
0070: public class TreePacksPanel extends IzPanel implements
0071: PacksPanelInterface {
0072: /**
0073: * Required (serializable)
0074: */
0075: private static final long serialVersionUID = 5684716698930628262L;
0076:
0077: // Common used Swing fields
0078: /**
0079: * The free space label.
0080: */
0081: protected JLabel freeSpaceLabel;
0082:
0083: /**
0084: * The space label.
0085: */
0086: protected JLabel spaceLabel;
0087:
0088: /**
0089: * The tip label.
0090: */
0091: protected JTextArea descriptionArea;
0092:
0093: /**
0094: * The dependencies label.
0095: */
0096: protected JTextArea dependencyArea;
0097:
0098: /**
0099: * The packs tree.
0100: */
0101: protected JTree packsTree;
0102:
0103: /**
0104: * The packs model.
0105: */
0106: protected PacksModel packsModel;
0107:
0108: /**
0109: * The tablescroll.
0110: */
0111: protected JScrollPane tableScroller;
0112:
0113: // Non-GUI fields
0114: /**
0115: * Map that connects names with pack objects
0116: */
0117: private Map<String, Pack> names;
0118:
0119: /**
0120: * The bytes of the current pack.
0121: */
0122: protected long bytes = 0;
0123:
0124: /**
0125: * The free bytes of the current selected disk.
0126: */
0127: protected long freeBytes = 0;
0128:
0129: /**
0130: * Are there dependencies in the packs
0131: */
0132: protected boolean dependenciesExist = false;
0133:
0134: /**
0135: * The packs locale database.
0136: */
0137: private LocaleDatabase langpack = null;
0138:
0139: /**
0140: * The name of the XML file that specifies the panel langpack
0141: */
0142: private static final String LANG_FILE_NAME = "packsLang.xml";
0143:
0144: private HashMap<String, Pack> idToPack;
0145: private HashMap<String, ArrayList<String>> treeData;
0146: private HashMap<Pack, Integer> packToRowNumber;
0147:
0148: private HashMap<String, CheckBoxNode> idToCheckBoxNode = new HashMap<String, CheckBoxNode>();
0149: //private boolean created = false; // UNUSED
0150:
0151: private CheckTreeController checkTreeController;
0152:
0153: /**
0154: * The constructor.
0155: *
0156: * @param parent The parent window.
0157: * @param idata The installation data.
0158: */
0159: public TreePacksPanel(InstallerFrame parent, InstallData idata) {
0160: super (parent, idata);
0161: // Load langpack.
0162: try {
0163: this .langpack = parent.langpack;
0164: InputStream langPackStream;
0165: String webdir = idata.info.getWebDirURL();
0166: if (webdir != null) {
0167: try {
0168: java.net.URL url = new java.net.URL(webdir
0169: + "/langpacks/" + LANG_FILE_NAME
0170: + idata.localeISO3);
0171: langPackStream = new WebAccessor(null)
0172: .openInputStream(url);
0173: } catch (Exception e) {
0174: langPackStream = ResourceManager.getInstance()
0175: .getInputStream(LANG_FILE_NAME);
0176: }
0177: } else
0178: langPackStream = ResourceManager.getInstance()
0179: .getInputStream(LANG_FILE_NAME);
0180:
0181: this .langpack.add(langPackStream);
0182: langPackStream.close();
0183: } catch (Throwable exception) {
0184: Debug.trace(exception);
0185: }
0186:
0187: // init the map
0188: computePacks(idata.availablePacks);
0189:
0190: }
0191:
0192: /**
0193: * The Implementation of this method should create the layout for the current class.
0194: */
0195:
0196: protected void createNormalLayout() {
0197: this .removeAll();
0198: setLayout(new BoxLayout(this , BoxLayout.Y_AXIS));
0199: createLabel("PacksPanel.info", "preferences", null, null);
0200: add(Box.createRigidArea(new Dimension(0, 3)));
0201: createLabel("PacksPanel.tip", "tip", null, null);
0202: add(Box.createRigidArea(new Dimension(0, 5)));
0203: tableScroller = new JScrollPane();
0204: packsTree = createPacksTree(300, tableScroller, null, null);
0205: if (dependenciesExist)
0206: dependencyArea = createTextArea(
0207: "PacksPanel.dependencyList", null, null, null);
0208: descriptionArea = createTextArea("PacksPanel.description",
0209: null, null, null);
0210: spaceLabel = createPanelWithLabel("PacksPanel.space", null,
0211: null);
0212: if (IoHelper.supported("getFreeSpace")) {
0213: add(Box.createRigidArea(new Dimension(0, 3)));
0214: freeSpaceLabel = createPanelWithLabel(
0215: "PacksPanel.freespace", null, null);
0216: }
0217: }
0218:
0219: /*
0220: * (non-Javadoc)
0221: *
0222: * @see com.izforge.izpack.panels.PacksPanelInterface#getLangpack()
0223: */
0224: public LocaleDatabase getLangpack() {
0225: return (langpack);
0226: }
0227:
0228: /*
0229: * (non-Javadoc)
0230: *
0231: * @see com.izforge.izpack.panels.PacksPanelInterface#getBytes()
0232: */
0233: public long getBytes() {
0234: return (bytes);
0235: }
0236:
0237: /*
0238: * (non-Javadoc)
0239: *
0240: * @see com.izforge.izpack.panels.PacksPanelInterface#setBytes(int)
0241: */
0242: public void setBytes(long bytes) {
0243: this .bytes = bytes;
0244: }
0245:
0246: /*
0247: * (non-Javadoc)
0248: *
0249: * @see com.izforge.izpack.panels.PacksPanelInterface#showSpaceRequired()
0250: */
0251: public void showSpaceRequired() {
0252: if (spaceLabel != null)
0253: spaceLabel.setText(Pack.toByteUnitsString(bytes));
0254: }
0255:
0256: /*
0257: * (non-Javadoc)
0258: *
0259: * @see com.izforge.izpack.panels.PacksPanelInterface#showFreeSpace()
0260: */
0261: public void showFreeSpace() {
0262: if (IoHelper.supported("getFreeSpace")
0263: && freeSpaceLabel != null) {
0264: String msg = null;
0265: freeBytes = IoHelper
0266: .getFreeSpace(IoHelper.existingParent(
0267: new File(idata.getInstallPath()))
0268: .getAbsolutePath());
0269: if (freeBytes < 0)
0270: msg = parent.langpack
0271: .getString("PacksPanel.notAscertainable");
0272: else
0273: msg = Pack.toByteUnitsString(freeBytes);
0274: freeSpaceLabel.setText(msg);
0275: }
0276: }
0277:
0278: public Debugger getDebugger() {
0279: return null;
0280: }
0281:
0282: /**
0283: * Indicates wether the panel has been validated or not.
0284: *
0285: * @return true if the needed space is less than the free space, else false
0286: */
0287: public boolean isValidated() {
0288: refreshPacksToInstall();
0289: if (IoHelper.supported("getFreeSpace") && freeBytes >= 0
0290: && freeBytes <= bytes) {
0291: JOptionPane.showMessageDialog(this , parent.langpack
0292: .getString("PacksPanel.notEnoughSpace"),
0293: parent.langpack.getString("installer.error"),
0294: JOptionPane.ERROR_MESSAGE);
0295: return (false);
0296: }
0297: return (true);
0298: }
0299:
0300: /**
0301: * Asks to make the XML panel data.
0302: *
0303: * @param panelRoot The XML tree to write the data in.
0304: */
0305: public void makeXMLData(XMLElement panelRoot) {
0306: new ImgPacksPanelAutomationHelper().makeXMLData(idata,
0307: panelRoot);
0308: }
0309:
0310: /**
0311: * This method tries to resolve the localized name of the given pack. If this is not possible,
0312: * the name given in the installation description file in ELEMENT <pack> will be used.
0313: *
0314: * @param pack for which the name should be resolved
0315: * @return localized name of the pack
0316: */
0317: private String getI18NPackName(Pack pack) {
0318: // Internationalization code
0319: String packName = pack.name;
0320: String key = pack.id;
0321: if (langpack != null && pack.id != null && !"".equals(pack.id)) {
0322: packName = langpack.getString(key);
0323: }
0324: if ("".equals(packName) || key == null || key.equals(packName)) {
0325: packName = pack.name;
0326: }
0327: return (packName);
0328: }
0329:
0330: public String getI18NPackName(String packId) {
0331: Pack pack = idToPack.get(packId);
0332: if (pack == null)
0333: return packId;
0334: // Internationalization code
0335: String packName = pack.name;
0336: String key = pack.id;
0337: if (langpack != null && pack.id != null && !"".equals(pack.id)) {
0338: packName = langpack.getString(key);
0339: }
0340: if ("".equals(packName) || key == null || key.equals(packName)) {
0341: packName = pack.name;
0342: }
0343: return (packName);
0344: }
0345:
0346: /**
0347: * Layout helper method:<br>
0348: * Creates an label with a message given by msgId and an icon given by the iconId. If layout and
0349: * constraints are not null, the label will be added to layout with the given constraints. The
0350: * label will be added to this object.
0351: *
0352: * @param msgId identifier for the IzPack langpack
0353: * @param iconId identifier for the IzPack icons
0354: * @param layout layout to be used
0355: * @param constraints constraints to be used
0356: * @return the created label
0357: */
0358: protected JLabel createLabel(String msgId, String iconId,
0359: GridBagLayout layout, GridBagConstraints constraints) {
0360: JLabel label = LabelFactory.create(parent.langpack
0361: .getString(msgId), parent.icons.getImageIcon(iconId),
0362: TRAILING);
0363: if (layout != null && constraints != null)
0364: layout.addLayoutComponent(label, constraints);
0365: add(label);
0366: return (label);
0367: }
0368:
0369: /**
0370: * Creates a panel containing a anonymous label on the left with the message for the given msgId
0371: * and a label on the right side with initial no text. The right label will be returned. If
0372: * layout and constraints are not null, the label will be added to layout with the given
0373: * constraints. The panel will be added to this object.
0374: *
0375: * @param msgId identifier for the IzPack langpack
0376: * @param layout layout to be used
0377: * @param constraints constraints to be used
0378: * @return the created (right) label
0379: */
0380: protected JLabel createPanelWithLabel(String msgId,
0381: GridBagLayout layout, GridBagConstraints constraints) {
0382: JPanel panel = new JPanel();
0383: JLabel label = new JLabel();
0384: if (label == null)
0385: label = new JLabel("");
0386: panel.setAlignmentX(LEFT_ALIGNMENT);
0387: panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
0388: panel
0389: .add(LabelFactory.create(parent.langpack
0390: .getString(msgId)));
0391: panel.add(Box.createHorizontalGlue());
0392: panel.add(label);
0393: if (layout != null && constraints != null)
0394: layout.addLayoutComponent(panel, constraints);
0395: add(panel);
0396: return (label);
0397: }
0398:
0399: private void refreshPacksToInstall() {
0400: idata.selectedPacks.clear();
0401: CheckBoxNode cbn = (CheckBoxNode) getTree().getModel()
0402: .getRoot();
0403: Enumeration e = cbn.depthFirstEnumeration();
0404: while (e.hasMoreElements()) {
0405: CheckBoxNode c = (CheckBoxNode) e.nextElement();
0406: if (c.isSelected() || c.isPartial()) {
0407: idata.selectedPacks.add(c.getPack());
0408: }
0409: }
0410: }
0411:
0412: /**
0413: * Creates a text area with standard settings and the title given by the msgId. If scroller is
0414: * not null, the create text area will be added to the scroller and the scroller to this object,
0415: * else the text area will be added directly to this object. If layout and constraints are not
0416: * null, the text area or scroller will be added to layout with the given constraints. The text
0417: * area will be returned.
0418: *
0419: * @param msgId identifier for the IzPack langpack
0420: * @param scroller the scroller to be used
0421: * @param layout layout to be used
0422: * @param constraints constraints to be used
0423: * @return the created text area
0424: */
0425: protected JTextArea createTextArea(String msgId,
0426: JScrollPane scroller, GridBagLayout layout,
0427: GridBagConstraints constraints) {
0428: JTextArea area = new JTextArea();
0429: // area.setMargin(new Insets(2, 2, 2, 2));
0430: area.setAlignmentX(LEFT_ALIGNMENT);
0431: area.setCaretPosition(0);
0432: area.setEditable(false);
0433: area.setEditable(false);
0434: area.setOpaque(false);
0435: area.setLineWrap(true);
0436: area.setWrapStyleWord(true);
0437: area.setBorder(BorderFactory.createTitledBorder(parent.langpack
0438: .getString(msgId)));
0439: area.setFont(getControlTextFont());
0440:
0441: if (layout != null && constraints != null) {
0442: if (scroller != null) {
0443: layout.addLayoutComponent(scroller, constraints);
0444: } else
0445: layout.addLayoutComponent(area, constraints);
0446: }
0447: if (scroller != null) {
0448: scroller.setViewportView(area);
0449: add(scroller);
0450: } else
0451: add(area);
0452: return (area);
0453:
0454: }
0455:
0456: /**
0457: * FIXME Creates the JTree component and calls all initialization tasks
0458: *
0459: * @param width
0460: * @param scroller
0461: * @param layout
0462: * @param constraints
0463: * @return
0464: */
0465: protected JTree createPacksTree(int width, JScrollPane scroller,
0466: GridBagLayout layout, GridBagConstraints constraints) {
0467: JTree tree = new JTree((CheckBoxNode) populateTreePacks(null));
0468: packsTree = tree;
0469: tree.setCellRenderer(new CheckBoxNodeRenderer(this ));
0470: tree.setEditable(false);
0471: tree.setShowsRootHandles(true);
0472: tree.setRootVisible(false);
0473: checkTreeController = new CheckTreeController(this );
0474: tree.addMouseListener(checkTreeController);
0475: tree.setBorder(BorderFactory.createEmptyBorder(0, 2, 0, 2));
0476: tree.setBackground(Color.white);
0477: tree.setToggleClickCount(0);
0478: //tree.setRowHeight(0);
0479:
0480: //table.getSelectionModel().addTreeSelectionListener(this);
0481: scroller.setViewportView(tree);
0482: scroller.setAlignmentX(LEFT_ALIGNMENT);
0483: scroller.getViewport().setBackground(Color.white);
0484: scroller.setPreferredSize(new Dimension(width,
0485: (idata.guiPrefs.height / 3 + 30)));
0486:
0487: if (layout != null && constraints != null)
0488: layout.addLayoutComponent(scroller, constraints);
0489: add(scroller);
0490: return (tree);
0491: }
0492:
0493: /**
0494: * Computes pack related data like the names or the dependencies state.
0495: *
0496: * @param packs
0497: */
0498: private void computePacks(List packs) {
0499: names = new HashMap<String, Pack>();
0500: dependenciesExist = false;
0501: for (Object pack1 : packs) {
0502: Pack pack = (Pack) pack1;
0503: names.put(pack.name, pack);
0504: if (pack.dependencies != null || pack.excludeGroup != null) {
0505: dependenciesExist = true;
0506: }
0507: }
0508: }
0509:
0510: /**
0511: * Refresh tree data from the PacksModel. This functions serves as a bridge
0512: * between the flat PacksModel and the tree data model.
0513: *
0514: */
0515: public void fromModel() {
0516: TreeModel model = this .packsTree.getModel();
0517: CheckBoxNode root = (CheckBoxNode) model.getRoot();
0518: updateModel(root);
0519: }
0520:
0521: private int getRowIndex(Pack pack) {
0522: Object o = packToRowNumber.get(pack);
0523: if (o == null)
0524: return -1;
0525: Integer ret = (Integer) o;
0526: return ret;
0527: }
0528:
0529: /**
0530: * Helper function for fromModel() - runs the recursion
0531: *
0532: * @param rnode
0533: */
0534: private void updateModel(CheckBoxNode rnode) {
0535: int rowIndex = getRowIndex(rnode.getPack());
0536: if (rowIndex > 0) {
0537: Integer state = (Integer) packsModel
0538: .getValueAt(rowIndex, 0);
0539: if ((state == -2) && rnode.getChildCount() > 0) {
0540: boolean dirty = false;
0541: Enumeration toBeDeselected = rnode
0542: .depthFirstEnumeration();
0543: while (toBeDeselected.hasMoreElements()) {
0544: CheckBoxNode cbn = (CheckBoxNode) toBeDeselected
0545: .nextElement();
0546: boolean chDirty = cbn.isSelected()
0547: || cbn.isPartial() || cbn.isEnabled();
0548: dirty = dirty || chDirty;
0549: if (chDirty) {
0550: cbn.setPartial(false);
0551: cbn.setSelected(false);
0552: cbn.setEnabled(false);
0553: setModelValue(cbn);
0554: }
0555: }
0556: if (dirty)
0557: fromModel();
0558: return;
0559: }
0560: }
0561:
0562: Enumeration e = rnode.children();
0563: while (e.hasMoreElements()) {
0564: Object next = e.nextElement();
0565: CheckBoxNode cbnode = (CheckBoxNode) next;
0566: String nodeText = cbnode.getId();
0567: Object nodePack = idToPack.get(nodeText);
0568: if (!cbnode.isPartial()) {
0569: int childRowIndex = getRowIndex((Pack) nodePack);
0570: if (childRowIndex > 0) {
0571: Integer state = (Integer) packsModel.getValueAt(
0572: childRowIndex, 0);
0573: cbnode.setEnabled(state >= 0);
0574: cbnode.setSelected(Math.abs(state.intValue()) == 1);
0575: }
0576: }
0577: updateModel(cbnode);
0578: }
0579: }
0580:
0581: /**
0582: * Updates a value for pack in PacksModel with data from a checkbox node
0583: *
0584: * @param cbnode This is the checkbox node which contains model values
0585: */
0586: public void setModelValue(CheckBoxNode cbnode) {
0587: String id = cbnode.getId();
0588: Object nodePack = idToPack.get(id);
0589: int value = 0;
0590: if (cbnode.isEnabled() && cbnode.isSelected())
0591: value = 1;
0592: if (!cbnode.isEnabled() && cbnode.isSelected())
0593: value = -1;
0594: if (!cbnode.isEnabled() && !cbnode.isSelected())
0595: value = -2;
0596: int rowIndex = getRowIndex((Pack) nodePack);
0597: if (rowIndex > 0) {
0598: Integer newValue = value;
0599: Integer modelValue = (Integer) packsModel.getValueAt(
0600: rowIndex, 0);
0601: if (!newValue.equals(modelValue))
0602: packsModel.setValueAt(newValue, rowIndex, 0);
0603: }
0604: }
0605:
0606: /**
0607: * Initialize tree model sructures
0608: *
0609: */
0610: private void createTreeData() {
0611: treeData = new HashMap<String, ArrayList<String>>();
0612: idToPack = new HashMap<String, Pack>();
0613:
0614: java.util.Iterator iter = idata.availablePacks.iterator();
0615: while (iter.hasNext()) {
0616: Pack p = (Pack) iter.next();
0617: idToPack.put(p.id, p);
0618: if (p.parent != null) {
0619: ArrayList<String> kids = null;
0620: if (treeData.containsKey(p.parent))
0621: kids = treeData.get(p.parent);
0622: else {
0623: kids = new ArrayList<String>();
0624: }
0625: kids.add(p.id);
0626: treeData.put(p.parent, kids);
0627: }
0628: }
0629: }
0630:
0631: /**
0632: * Shows and updates the description text in the panel
0633: *
0634: * @param id
0635: */
0636: public void setDescription(String id) {
0637: VariableSubstitutor vs = new VariableSubstitutor(idata
0638: .getVariables());
0639: if (descriptionArea != null) {
0640: Pack pack = idToPack.get(id);
0641: String desc = "";
0642: String key = pack.id + ".description";
0643: if (langpack != null && pack.id != null
0644: && !"".equals(pack.id)) {
0645: desc = langpack.getString(key);
0646: }
0647: if ("".equals(desc) || key.equals(desc)) {
0648: desc = pack.description;
0649: }
0650: desc = vs.substitute(desc, null);
0651: descriptionArea.setText(desc);
0652: }
0653: }
0654:
0655: /**
0656: * Shows and updates the dependencies text in the panel
0657: *
0658: * @param id
0659: */
0660: public void setDependencies(String id) {
0661: if (dependencyArea != null) {
0662: Pack pack = idToPack.get(id);
0663: List<String> dep = pack.dependencies;
0664: String list = "";
0665: if (dep != null) {
0666: list += (langpack == null) ? "Dependencies: "
0667: : langpack.getString("PacksPanel.dependencies");
0668: }
0669: for (int j = 0; dep != null && j < dep.size(); j++) {
0670: String name = dep.get(j);
0671: list += getI18NPackName(names.get(name));
0672: if (j != dep.size() - 1)
0673: list += ", ";
0674: }
0675:
0676: // add the list of the packs to be excluded
0677: String excludeslist = (langpack == null) ? "Excludes: "
0678: : langpack.getString("PacksPanel.excludes");
0679: int numexcludes = 0;
0680: int i = getRowIndex(pack);
0681: if (pack.excludeGroup != null) {
0682: for (int q = 0; q < idata.availablePacks.size(); q++) {
0683: Pack otherpack = (Pack) idata.availablePacks.get(q);
0684: String exgroup = otherpack.excludeGroup;
0685: if (exgroup != null) {
0686: if (q != i && pack.excludeGroup.equals(exgroup)) {
0687:
0688: excludeslist += getI18NPackName(otherpack)
0689: + ", ";
0690: numexcludes++;
0691: }
0692: }
0693: }
0694: }
0695: // concatenate
0696: if (dep != null)
0697: excludeslist = " " + excludeslist;
0698: if (numexcludes > 0)
0699: list += excludeslist;
0700: if (list.endsWith(", "))
0701: list = list.substring(0, list.length() - 2);
0702:
0703: // and display the result
0704: dependencyArea.setText(list);
0705: }
0706: }
0707:
0708: /**
0709: * Gives a CheckBoxNode instance from the id
0710: *
0711: * @param id
0712: * @return
0713: */
0714: public CheckBoxNode getCbnById(String id) {
0715: return this .idToCheckBoxNode.get(id);
0716: }
0717:
0718: /**
0719: * Reads the available packs and creates the JTree structure based on
0720: * the parent definitions.
0721: *
0722: * @param parent
0723: * @return
0724: */
0725: private Object populateTreePacks(String parent) {
0726: if (parent == null) // the root node
0727: {
0728: java.util.Iterator iter = idata.availablePacks.iterator();
0729: ArrayList rootNodes = new ArrayList();
0730: while (iter.hasNext()) {
0731: Pack p = (Pack) iter.next();
0732: if (p.parent == null) {
0733: rootNodes.add(populateTreePacks(p.id));
0734: }
0735: }
0736: TreeNode nv = new CheckBoxNode("Root", "Root", rootNodes
0737: .toArray(), true);
0738: return nv;
0739: } else {
0740: ArrayList links = new ArrayList();
0741: Object kidsObject = treeData.get(parent);
0742: Pack p = idToPack.get(parent);
0743: String translated = getI18NPackName(parent);
0744:
0745: if (kidsObject != null) {
0746: ArrayList kids = (ArrayList) kidsObject;
0747: for (Object kid : kids) {
0748: String kidId = (String) kid;
0749: links.add(populateTreePacks(kidId));
0750: }
0751:
0752: CheckBoxNode cbn = new CheckBoxNode(parent, translated,
0753: links.toArray(), true);
0754: idToCheckBoxNode.put(cbn.getId(), cbn);
0755: cbn.setPack(p);
0756: cbn.setTotalSize(p.nbytes);
0757: return cbn;
0758: } else {
0759: CheckBoxNode cbn = new CheckBoxNode(parent, translated,
0760: true);
0761: idToCheckBoxNode.put(cbn.getId(), cbn);
0762: cbn.setPack(p);
0763: cbn.setTotalSize(p.nbytes);
0764: return cbn;
0765: }
0766: }
0767: }
0768:
0769: /**
0770: * Called when the panel becomes active. If a derived class implements this method also, it is
0771: * recomanded to call this method with the super operator first.
0772: */
0773: public void panelActivate() {
0774: try {
0775:
0776: // TODO the PacksModel could be patched such that isCellEditable
0777: // allows returns false. In that case the PacksModel must not be
0778: // adapted here.
0779: packsModel = new PacksModel(this , idata, this .parent
0780: .getRules()) {
0781: /**
0782: * Required (serializable)
0783: */
0784: private static final long serialVersionUID = 697462278279845304L;
0785:
0786: public boolean isCellEditable(int rowIndex,
0787: int columnIndex) {
0788: return false;
0789: }
0790: };
0791:
0792: //initialize helper map to increa performance
0793: packToRowNumber = new HashMap<Pack, Integer>();
0794: java.util.Iterator rowpack = idata.availablePacks
0795: .iterator();
0796: while (rowpack.hasNext()) {
0797: Pack p = (Pack) rowpack.next();
0798: packToRowNumber.put(p, idata.availablePacks.indexOf(p));
0799: }
0800:
0801: // Init tree structures
0802: createTreeData();
0803:
0804: // Create panel GUI (and populate the TJtree)
0805: createNormalLayout();
0806:
0807: // Reload the data from the PacksModel into the tree in order the initial
0808: // dependencies to be resolved and effective
0809: fromModel();
0810:
0811: // Init the pack sizes (individual and cumulative)
0812: CheckBoxNode root = (CheckBoxNode) packsTree.getModel()
0813: .getRoot();
0814: checkTreeController.updateAllParents(root);
0815: CheckTreeController.initTotalSize(root, false);
0816:
0817: // Ugly repaint because of a bug in tree.treeDidChange
0818: packsTree.revalidate();
0819: packsTree.repaint();
0820:
0821: tableScroller.setColumnHeaderView(null);
0822: tableScroller.setColumnHeader(null);
0823:
0824: // set the JCheckBoxes to the currently selected panels. The
0825: // selection might have changed in another panel
0826: java.util.Iterator iter = idata.availablePacks.iterator();
0827: bytes = 0;
0828: while (iter.hasNext()) {
0829: Pack p = (Pack) iter.next();
0830: if (p.required) {
0831: bytes += p.nbytes;
0832: continue;
0833: }
0834: if (idata.selectedPacks.contains(p))
0835: bytes += p.nbytes;
0836: }
0837: } catch (Exception e) {
0838: e.printStackTrace();
0839: }
0840: showSpaceRequired();
0841: showFreeSpace();
0842: }
0843:
0844: /*
0845: * (non-Javadoc)
0846: *
0847: * @see com.izforge.izpack.installer.IzPanel#getSummaryBody()
0848: */
0849: public String getSummaryBody() {
0850: StringBuffer retval = new StringBuffer(256);
0851: Iterator iter = idata.selectedPacks.iterator();
0852: boolean first = true;
0853: while (iter.hasNext()) {
0854: if (!first) {
0855: retval.append("<br>");
0856: }
0857: first = false;
0858: Pack pack = (Pack) iter.next();
0859: if (langpack != null && pack.id != null
0860: && !"".equals(pack.id)) {
0861: retval.append(langpack.getString(pack.id));
0862: } else
0863: retval.append(pack.name);
0864: }
0865: return (retval.toString());
0866: }
0867:
0868: public JTree getTree() {
0869: return packsTree;
0870: }
0871:
0872: }
0873:
0874: /**
0875: *
0876: * The renderer model for individual checkbox nodes in a JTree. It renders the
0877: * checkbox and a label for the pack size.
0878: *
0879: * @author <a href="vralev@redhat.com">Vladimir Ralev</a>
0880: * @version $Revision: 1.1 $
0881: */
0882: class CheckBoxNodeRenderer implements TreeCellRenderer {
0883: private static final JPanel rendererPanel = new JPanel();
0884: private static final JLabel packSizeLabel = new JLabel();
0885: private static final JCheckBox checkbox = new JCheckBox();
0886: private static final JCheckBox normalCheckBox = new JCheckBox();
0887: private static final java.awt.Font normalFont = new JCheckBox()
0888: .getFont();
0889: private static final java.awt.Font boldFont = new java.awt.Font(
0890: normalFont.getFontName(), java.awt.Font.BOLD, normalFont
0891: .getSize());
0892: private static final java.awt.Font plainFont = new java.awt.Font(
0893: normalFont.getFontName(), java.awt.Font.PLAIN, normalFont
0894: .getSize());
0895: private static final Color annotationColor = new Color(0, 0, 120); // red
0896: private static final Color changedColor = new Color(200, 0, 0);
0897:
0898: private static Color selectionForeground, selectionBackground,
0899: textForeground, textBackground;
0900:
0901: TreePacksPanel treePacksPanel;
0902:
0903: public CheckBoxNodeRenderer(TreePacksPanel t) {
0904: selectionForeground = UIManager
0905: .getColor("Tree.selectionForeground");
0906: selectionBackground = UIManager
0907: .getColor("Tree.selectionBackground");
0908: textForeground = UIManager.getColor("Tree.textForeground");
0909: textBackground = UIManager.getColor("Tree.textBackground");
0910: treePacksPanel = t;
0911:
0912: int treeWidth = t.getTree().getPreferredSize().width;
0913: int height = checkbox.getPreferredSize().height;
0914: int cellWidth = treeWidth - treeWidth / 4;
0915:
0916: //Don't touch, it fixes various layout bugs in swing/awt
0917: rendererPanel.setLayout(new java.awt.BorderLayout(0, 0));
0918: rendererPanel.setBackground(textBackground);
0919: rendererPanel.add(java.awt.BorderLayout.WEST, checkbox);
0920:
0921: rendererPanel.setAlignmentX((float) 0);
0922: rendererPanel.setAlignmentY((float) 0);
0923: rendererPanel.add(java.awt.BorderLayout.EAST, packSizeLabel);
0924:
0925: rendererPanel.setMinimumSize(new Dimension(cellWidth, height));
0926: rendererPanel
0927: .setPreferredSize(new Dimension(cellWidth, height));
0928: rendererPanel.setSize(new Dimension(cellWidth, height));
0929:
0930: rendererPanel.setBorder(BorderFactory.createEmptyBorder(0, 0,
0931: 0, 0));
0932: }
0933:
0934: public Component getTreeCellRendererComponent(JTree tree,
0935: Object value, boolean selected, boolean expanded,
0936: boolean leaf, int row, boolean hasFocus) {
0937: treePacksPanel.fromModel();
0938:
0939: if (selected) {
0940: checkbox.setForeground(selectionForeground);
0941: checkbox.setBackground(selectionBackground);
0942: rendererPanel.setForeground(selectionForeground);
0943: rendererPanel.setBackground(selectionBackground);
0944: packSizeLabel.setBackground(selectionBackground);
0945: } else {
0946: checkbox.setForeground(textForeground);
0947: checkbox.setBackground(textBackground);
0948: rendererPanel.setForeground(textForeground);
0949: rendererPanel.setBackground(textBackground);
0950: packSizeLabel.setBackground(textBackground);
0951: }
0952:
0953: if ((value != null) && (value instanceof CheckBoxNode)) {
0954: CheckBoxNode node = (CheckBoxNode) value;
0955:
0956: if (node.isTotalSizeChanged())
0957: packSizeLabel.setForeground(changedColor);
0958: else {
0959: if (selected)
0960: packSizeLabel.setForeground(selectionForeground);
0961: else {
0962: packSizeLabel.setForeground(annotationColor);
0963: }
0964: }
0965:
0966: checkbox.setText(node.getTranslatedText());
0967:
0968: packSizeLabel.setText(Pack.toByteUnitsString(node
0969: .getTotalSize()));
0970:
0971: if (node.isPartial())
0972: checkbox.setSelected(false);
0973: else
0974: checkbox.setSelected(node.isSelected());
0975:
0976: checkbox.setEnabled(node.isEnabled());
0977: packSizeLabel.setEnabled(node.isEnabled());
0978:
0979: if (node.getChildCount() > 0) {
0980: checkbox.setFont(boldFont);
0981: packSizeLabel.setFont(boldFont);
0982: } else {
0983: checkbox.setFont(normalFont);
0984: packSizeLabel.setFont(plainFont);
0985: }
0986:
0987: if (node.isPartial()) {
0988: checkbox.setIcon(new PartialIcon());
0989: } else {
0990: checkbox.setIcon(normalCheckBox.getIcon());
0991: }
0992: }
0993: return rendererPanel;
0994: }
0995:
0996: public Component getCheckRenderer() {
0997: return rendererPanel;
0998: }
0999:
1000: }
1001:
1002: /**
1003: *
1004: * The model structure for a JTree node.
1005: *
1006: * @author <a href="vralev@redhat.com">Vladimir Ralev</a>
1007: * @version $Revision: 1.1 $
1008: */
1009: class CheckBoxNode extends DefaultMutableTreeNode {
1010:
1011: /**
1012: * Required (serializable)
1013: */
1014: private static final long serialVersionUID = 8743154051564336973L;
1015: String id;
1016: boolean selected;
1017: boolean partial;
1018: boolean enabled;
1019: boolean totalSizeChanged;
1020: String translatedText;
1021: Pack pack;
1022: long totalSize;
1023:
1024: public CheckBoxNode(String id, String translated, boolean selected) {
1025: this .id = id;
1026: this .selected = selected;
1027: this .translatedText = translated;
1028: }
1029:
1030: public CheckBoxNode(String id, String translated,
1031: Object elements[], boolean selected) {
1032: this .id = id;
1033: this .translatedText = translated;
1034: for (int i = 0, n = elements.length; i < n; i++) {
1035: CheckBoxNode tn = (CheckBoxNode) elements[i];
1036: add(tn);
1037: }
1038: }
1039:
1040: public boolean isLeaf() {
1041: return this .getChildCount() == 0;
1042: }
1043:
1044: public boolean isSelected() {
1045: return selected;
1046: }
1047:
1048: public void setSelected(boolean newValue) {
1049: selected = newValue;
1050: }
1051:
1052: public String getId() {
1053: return id;
1054: }
1055:
1056: public void setId(String newValue) {
1057: id = newValue;
1058: }
1059:
1060: public String toString() {
1061: return getClass().getName() + "[" + id + "/" + selected + "]";
1062: }
1063:
1064: public boolean isPartial() {
1065: return partial;
1066: }
1067:
1068: public void setPartial(boolean partial) {
1069: this .partial = partial;
1070: if (partial)
1071: setSelected(true);
1072: }
1073:
1074: public boolean isEnabled() {
1075: return enabled;
1076: }
1077:
1078: public void setEnabled(boolean enabled) {
1079: this .enabled = enabled;
1080: }
1081:
1082: public String getTranslatedText() {
1083: return translatedText;
1084: }
1085:
1086: public void setTranslatedText(String translatedText) {
1087: this .translatedText = translatedText;
1088: }
1089:
1090: public Pack getPack() {
1091: return pack;
1092: }
1093:
1094: public void setPack(Pack pack) {
1095: this .pack = pack;
1096: }
1097:
1098: public long getTotalSize() {
1099: return totalSize;
1100: }
1101:
1102: public void setTotalSize(long totalSize) {
1103: this .totalSize = totalSize;
1104: }
1105:
1106: public boolean isTotalSizeChanged() {
1107: return totalSizeChanged;
1108: }
1109:
1110: public void setTotalSizeChanged(boolean totalSizeChanged) {
1111: this .totalSizeChanged = totalSizeChanged;
1112: }
1113: }
1114:
1115: /**
1116: *
1117: * Special checkbox icon which shows partially selected nodes.
1118: *
1119: * @author <a href="vralev@redhat.com">Vladimir Ralev</a>
1120: * @version $Revision: 1.1 $
1121: */
1122: class PartialIcon implements Icon {
1123: protected int getControlSize() {
1124: return 13;
1125: }
1126:
1127: public void paintIcon(Component c, Graphics g, int x, int y) {
1128: int controlSize = getControlSize();
1129: g.setColor(MetalLookAndFeel.getControlShadow());
1130: g.fillRect(x, y, controlSize - 1, controlSize - 1);
1131: drawBorder(g, x, y, controlSize, controlSize);
1132:
1133: g.setColor(Color.green);
1134: drawCheck(c, g, x, y);
1135: }
1136:
1137: private void drawBorder(Graphics g, int x, int y, int w, int h) {
1138: g.translate(x, y);
1139:
1140: // outer frame rectangle
1141: g.setColor(MetalLookAndFeel.getControlDarkShadow());
1142: g.setColor(new Color(0.4f, 0.4f, 0.4f));
1143: g.drawRect(0, 0, w - 2, h - 2);
1144:
1145: // middle frame
1146: g.setColor(MetalLookAndFeel.getControlHighlight());
1147: g.setColor(new Color(0.6f, 0.6f, 0.6f));
1148: g.drawRect(1, 1, w - 2, h - 2);
1149:
1150: // background
1151: g.setColor(new Color(0.99f, 0.99f, 0.99f));
1152: g.fillRect(2, 2, w - 3, h - 3);
1153:
1154: //some extra lines for FX
1155: g.setColor(MetalLookAndFeel.getControl());
1156: g.drawLine(0, h - 1, 1, h - 2);
1157: g.drawLine(w - 1, 0, w - 2, 1);
1158: g.translate(-x, -y);
1159: }
1160:
1161: protected void drawCheck(Component c, Graphics g, int x, int y) {
1162: int controlSize = getControlSize();
1163: g.setColor(new Color(0.0f, 0.7f, 0.0f));
1164:
1165: g.fillOval(x + controlSize / 2 - 2, y + controlSize / 2 - 2, 6,
1166: 6);
1167: }
1168:
1169: public int getIconWidth() {
1170: return getControlSize();
1171: }
1172:
1173: public int getIconHeight() {
1174: return getControlSize();
1175: }
1176: }
1177:
1178: /**
1179: *
1180: * Controller class which handles the mouse clicks on checkbox nodes. Also
1181: * contains utility methods to update the sizes and the states of the nodes.
1182: *
1183: * @author <a href="vralev@redhat.com">Vladimir Ralev</a>
1184: * @version $Revision: 1.1 $
1185: */
1186: class CheckTreeController extends MouseAdapter {
1187: JTree tree;
1188: TreePacksPanel treePacksPanel;
1189: int checkWidth = new JCheckBox().getPreferredSize().width;
1190:
1191: public CheckTreeController(TreePacksPanel p) {
1192: this .tree = p.getTree();
1193: this .treePacksPanel = p;
1194: }
1195:
1196: private void selectNode(CheckBoxNode current) {
1197: current.setPartial(false);
1198: treePacksPanel.setModelValue(current);
1199: Enumeration e = current.depthFirstEnumeration();
1200: while (e.hasMoreElements()) {
1201: CheckBoxNode child = (CheckBoxNode) e.nextElement();
1202: child.setSelected(current.isSelected()
1203: || child.getPack().required);
1204: if (!child.isSelected())
1205: child.setPartial(false);
1206: treePacksPanel.setModelValue(child);
1207: }
1208: treePacksPanel.fromModel();
1209: }
1210:
1211: private boolean hasExcludes(CheckBoxNode node) {
1212: Enumeration e = node.depthFirstEnumeration();
1213: while (e.hasMoreElements()) {
1214: CheckBoxNode cbn = (CheckBoxNode) e.nextElement();
1215: if (cbn.getPack().excludeGroup != null)
1216: return true;
1217: }
1218: return false;
1219: }
1220:
1221: public void mouseReleased(MouseEvent me) {
1222: TreePath path = tree.getPathForLocation(me.getX(), me.getY());
1223: if (path == null)
1224: return;
1225: CheckBoxNode current = (CheckBoxNode) path
1226: .getLastPathComponent();
1227: treePacksPanel.setDescription(current.getId());
1228: treePacksPanel.setDependencies(current.getId());
1229: if (me.getX() > tree.getPathBounds(path).x + checkWidth)
1230: return;
1231:
1232: // If this pack is required, leave it alone
1233: if (current.getPack().required)
1234: return;
1235:
1236: boolean currIsSelected = current.isSelected()
1237: & !current.isPartial();
1238: boolean currIsPartial = current.isPartial();
1239: boolean currHasExcludes = hasExcludes(current);
1240: CheckBoxNode root = (CheckBoxNode) current.getRoot();
1241:
1242: if (currIsPartial && currHasExcludes) {
1243: current.setSelected(false);
1244: selectNode(current); // deselect actually
1245: updateAllParents(root);
1246: } else {
1247: if (!currIsSelected)
1248: selectAllChildNodes(current);
1249: current.setSelected(!currIsSelected);
1250: selectNode(current);
1251: updateAllParents(root);
1252: }
1253:
1254: initTotalSize(root, true);
1255:
1256: // must override the bytes being computed at packsModel
1257: treePacksPanel.setBytes((int) root.getTotalSize());
1258: treePacksPanel.showSpaceRequired();
1259: tree.treeDidChange();
1260: }
1261:
1262: public void selectAllChildNodes(CheckBoxNode cbn) {
1263: Enumeration e = cbn.children();
1264: while (e.hasMoreElements()) {
1265: CheckBoxNode subCbn = (CheckBoxNode) e.nextElement();
1266: selectAllDependencies(subCbn);
1267: if (subCbn.getChildCount() > 0)
1268: selectAllChildNodes(subCbn);
1269:
1270: subCbn.setSelected(true);
1271: // we need this, because the setModel ignored disabled values
1272: subCbn.setEnabled(true);
1273: treePacksPanel.setModelValue(subCbn);
1274: subCbn.setEnabled(!subCbn.getPack().required);
1275: }
1276: }
1277:
1278: public void selectAllDependencies(CheckBoxNode cbn) {
1279: Pack pack = cbn.getPack();
1280: List<String> deps = pack.getDependencies();
1281: if (deps == null)
1282: return;
1283: Iterator<String> e = deps.iterator();
1284: while (e.hasNext()) {
1285: String depId = e.next();
1286: CheckBoxNode depCbn = treePacksPanel.getCbnById(depId);
1287: selectAllDependencies(depCbn);
1288: if (depCbn.getChildCount() > 0) {
1289: if (!depCbn.isSelected() || depCbn.isPartial())
1290: selectAllChildNodes(depCbn);
1291: }
1292: depCbn.setSelected(true);
1293: // we need this, because the setModel ignored disabled values
1294: depCbn.setEnabled(true);
1295: treePacksPanel.setModelValue(depCbn);
1296: depCbn.setEnabled(!depCbn.getPack().required);
1297: }
1298: }
1299:
1300: /**
1301: * Updates partial/deselected/selected state of all parent nodes.
1302: * This is needed and is a patch to allow unrelated nodes (in terms of the tree)
1303: * to fire updates for each other.
1304: *
1305: * @param root
1306: */
1307: public void updateAllParents(CheckBoxNode root) {
1308: Enumeration rootEnum = root.depthFirstEnumeration();
1309: while (rootEnum.hasMoreElements()) {
1310: CheckBoxNode child = (CheckBoxNode) rootEnum.nextElement();
1311: if (child.getParent() != null
1312: && !child.getParent().equals(root))
1313: updateParents(child);
1314: }
1315: }
1316:
1317: /**
1318: * Updates the parents of this particular node
1319: *
1320: * @param node
1321: */
1322: private void updateParents(CheckBoxNode node) {
1323: CheckBoxNode parent = (CheckBoxNode) node.getParent();
1324: if (parent != null && !parent.equals(parent.getRoot())) {
1325: Enumeration ne = parent.children();
1326: boolean allSelected = true;
1327: boolean allDeselected = true;
1328: while (ne.hasMoreElements()) {
1329: CheckBoxNode child = (CheckBoxNode) ne.nextElement();
1330: if (child.isSelected())
1331: allDeselected = false;
1332: else
1333: allSelected = false;
1334: if (child.isPartial())
1335: allSelected = allDeselected = false;
1336: if (!allSelected && !allDeselected)
1337: break;
1338: }
1339: if (parent.getChildCount() > 0) {
1340: if (!allSelected && !allDeselected)
1341: setPartialParent(parent);
1342: else
1343: parent.setPartial(false);
1344: if (allSelected)
1345: parent.setSelected(true);
1346: if (allDeselected)
1347: parent.setSelected(false);
1348: treePacksPanel.setModelValue(parent);
1349: if (allSelected || allDeselected)
1350: updateParents(parent);
1351: }
1352: //updateTotalSize(node);
1353: }
1354: }
1355:
1356: public static void setPartialParent(CheckBoxNode node) {
1357: node.setPartial(true);
1358: CheckBoxNode parent = (CheckBoxNode) node.getParent();
1359: if (parent != null && !parent.equals(parent.getRoot()))
1360: setPartialParent(parent);
1361: }
1362:
1363: public static long initTotalSize(CheckBoxNode node,
1364: boolean markChanged) {
1365: if (node.isLeaf())
1366: return node.getPack().nbytes;
1367: Enumeration e = node.children();
1368: Pack nodePack = node.getPack();
1369: long bytes = 0;
1370: if (nodePack != null)
1371: bytes = nodePack.nbytes;
1372: while (e.hasMoreElements()) {
1373: CheckBoxNode c = (CheckBoxNode) e.nextElement();
1374: long size = initTotalSize(c, markChanged);
1375: if (c.isSelected() || c.isPartial()) {
1376: bytes += size;
1377: }
1378: }
1379: if (markChanged) {
1380: long old = node.getTotalSize();
1381: if (old != bytes)
1382: node.setTotalSizeChanged(true);
1383: else
1384: node.setTotalSizeChanged(false);
1385: }
1386: node.setTotalSize(bytes);
1387: return bytes;
1388: }
1389: }
|