001/* 002 * IzPack - Copyright 2001-2005 Julien Ponge, All Rights Reserved. 003 * 004 * http://www.izforge.com/izpack/ 005 * http://developer.berlios.de/projects/izpack/ 006 * 007 * Copyright 2002 Marcus Wolschon 008 * Copyright 2002 Jan Blok 009 * Copyright 2004 Klaus Bartz 010 * 011 * Licensed under the Apache License, Version 2.0 (the "License"); 012 * you may not use this file except in compliance with the License. 013 * You may obtain a copy of the License at 014 * 015 * http://www.apache.org/licenses/LICENSE-2.0 016 * 017 * Unless required by applicable law or agreed to in writing, software 018 * distributed under the License is distributed on an "AS IS" BASIS, 019 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 020 * See the License for the specific language governing permissions and 021 * limitations under the License. 022 */ 023 024package com.izforge.izpack.panels; 025 026import java.awt.Color; 027import java.awt.Component; 028import java.awt.Dimension; 029import java.awt.GridBagConstraints; 030import java.awt.GridBagLayout; 031import java.awt.Insets; 032import java.awt.event.ActionEvent; 033import java.awt.event.ActionListener; 034import java.io.File; 035import java.io.InputStream; 036import java.util.HashMap; 037import java.util.Iterator; 038import java.util.List; 039import java.util.Map; 040 041import javax.swing.AbstractCellEditor; 042import javax.swing.BorderFactory; 043import javax.swing.Box; 044import javax.swing.BoxLayout; 045import javax.swing.JCheckBox; 046import javax.swing.JLabel; 047import javax.swing.JOptionPane; 048import javax.swing.JPanel; 049import javax.swing.JScrollPane; 050import javax.swing.JTable; 051import javax.swing.JTextArea; 052import javax.swing.ListSelectionModel; 053import javax.swing.border.Border; 054import javax.swing.event.ListSelectionEvent; 055import javax.swing.event.ListSelectionListener; 056import javax.swing.table.DefaultTableCellRenderer; 057import javax.swing.table.TableCellEditor; 058import javax.swing.table.TableCellRenderer; 059 060import net.n3.nanoxml.XMLElement; 061 062import com.izforge.izpack.LocaleDatabase; 063import com.izforge.izpack.Pack; 064import com.izforge.izpack.gui.LabelFactory; 065import com.izforge.izpack.installer.InstallData; 066import com.izforge.izpack.installer.InstallerFrame; 067import com.izforge.izpack.installer.IzPanel; 068import com.izforge.izpack.installer.ResourceManager; 069import com.izforge.izpack.util.IoHelper; 070import com.izforge.izpack.util.Debug; 071 072/** 073 * The base class for Packs panels. It brings the common member and methods of the different packs 074 * panels together. This class handles the common logic of pack selection. The derived class should 075 * be create the layout and other specific actions. There are some helper methods to simplify layout 076 * creation in the derived class. 077 * 078 * @author Julien Ponge 079 * @author Klaus Bartz 080 */ 081public abstract class PacksPanelBase extends IzPanel implements PacksPanelInterface, 082 ListSelectionListener 083{ 084 085 // Common used Swing fields 086 /** The free space label. */ 087 protected JLabel freeSpaceLabel; 088 089 /** The space label. */ 090 protected JLabel spaceLabel; 091 092 /** The tip label. */ 093 protected JTextArea descriptionArea; 094 095 /** The dependencies label. */ 096 protected JTextArea dependencyArea; 097 098 /** The packs table. */ 099 protected JTable packsTable; 100 101 /** The tablescroll. */ 102 protected JScrollPane tableScroller; 103 104 // Non-GUI fields 105 /** Map that connects names with pack objects */ 106 private Map names; 107 108 /** The bytes of the current pack. */ 109 protected int bytes = 0; 110 111 /** The free bytes of the current selected disk. */ 112 protected long freeBytes = 0; 113 114 /** Are there dependencies in the packs */ 115 protected boolean dependenciesExist = false; 116 117 /** The packs locale database. */ 118 private LocaleDatabase langpack = null; 119 120 /** The name of the XML file that specifies the panel langpack */ 121 private static final String LANG_FILE_NAME = "packsLang.xml"; 122 123 /** 124 * The constructor. 125 * 126 * @param parent The parent window. 127 * @param idata The installation data. 128 */ 129 public PacksPanelBase(InstallerFrame parent, InstallData idata) 130 { 131 super(parent, idata); 132 // Load langpack. 133 try 134 { 135 this.langpack = parent.langpack; 136 InputStream inputStream = ResourceManager.getInstance().getInputStream(LANG_FILE_NAME); 137 this.langpack.add(inputStream); 138 } 139 catch (Throwable exception) 140 { 141 Debug.trace(exception); 142 } 143 // init the map 144 computePacks(idata.availablePacks); 145 146 createNormalLayout(); 147 } 148 149 /** 150 * The Implementation of this method should create the layout for the current class. 151 */ 152 abstract protected void createNormalLayout(); 153 154 /* 155 * (non-Javadoc) 156 * 157 * @see com.izforge.izpack.panels.PacksPanelInterface#getLangpack() 158 */ 159 public LocaleDatabase getLangpack() 160 { 161 return (langpack); 162 } 163 164 /* 165 * (non-Javadoc) 166 * 167 * @see com.izforge.izpack.panels.PacksPanelInterface#getBytes() 168 */ 169 public int getBytes() 170 { 171 return (bytes); 172 } 173 174 /* 175 * (non-Javadoc) 176 * 177 * @see com.izforge.izpack.panels.PacksPanelInterface#setBytes(int) 178 */ 179 public void setBytes(int bytes) 180 { 181 this.bytes = bytes; 182 } 183 184 /* 185 * (non-Javadoc) 186 * 187 * @see com.izforge.izpack.panels.PacksPanelInterface#showSpaceRequired() 188 */ 189 public void showSpaceRequired() 190 { 191 if (spaceLabel != null) spaceLabel.setText(Pack.toByteUnitsString(bytes)); 192 } 193 194 /* 195 * (non-Javadoc) 196 * 197 * @see com.izforge.izpack.panels.PacksPanelInterface#showFreeSpace() 198 */ 199 public void showFreeSpace() 200 { 201 if (IoHelper.supported("getFreeSpace") && freeSpaceLabel != null) 202 { 203 String msg = null; 204 freeBytes = IoHelper.getFreeSpace(IoHelper.existingParent( 205 new File(idata.getInstallPath())).getAbsolutePath()); 206 if (freeBytes > 0x000000007fffffff) 207 msg = " > 2 GB"; 208 else if (freeBytes < 0) 209 msg = parent.langpack.getString("PacksPanel.notAscertainable"); 210 else 211 msg = Pack.toByteUnitsString((int) freeBytes); 212 freeSpaceLabel.setText(msg); 213 } 214 } 215 216 /** 217 * Indicates wether the panel has been validated or not. 218 * 219 * @return true if the needed space is less than the free space, else false 220 */ 221 public boolean isValidated() 222 { 223 if (IoHelper.supported("getFreeSpace") && freeBytes >= 0 && freeBytes <= bytes) 224 { 225 JOptionPane.showMessageDialog(this, parent.langpack 226 .getString("PacksPanel.notEnoughSpace"), parent.langpack 227 .getString("installer.error"), JOptionPane.ERROR_MESSAGE); 228 return (false); 229 } 230 return (true); 231 } 232 233 /** 234 * Asks to make the XML panel data. 235 * 236 * @param panelRoot The XML tree to write the data in. 237 */ 238 public void makeXMLData(XMLElement panelRoot) 239 { 240 new ImgPacksPanelAutomationHelper().makeXMLData(idata, panelRoot); 241 } 242 243 /* 244 * (non-Javadoc) 245 * 246 * @see javax.swing.event.ListSelectionListener#valueChanged(javax.swing.event.ListSelectionEvent) 247 */ 248 public void valueChanged(ListSelectionEvent e) 249 { 250 int i = packsTable.getSelectedRow(); 251 if (i < 0) return; 252 // Operations for the description 253 if (descriptionArea != null) 254 { 255 Pack pack = (Pack) idata.availablePacks.get(i); 256 String desc = ""; 257 String key = pack.id + ".description"; 258 if (langpack != null && pack.id != null && !pack.id.equals("")) 259 { 260 desc = langpack.getString(key); 261 } 262 if (desc.equals("") || key.equals(desc)) 263 { 264 desc = pack.description; 265 } 266 descriptionArea.setText(desc); 267 } 268 // Operation for the dependency listing 269 if (dependencyArea != null) 270 { 271 Pack pack = (Pack) idata.availablePacks.get(i); 272 List dep = pack.dependencies; 273 String list = ""; 274 for (int j = 0; dep != null && j < dep.size(); j++) 275 { 276 String name = (String) dep.get(j); 277 // Internationalization code 278 Pack childPack = (Pack) names.get(name); 279 String childName = ""; 280 String key = childPack.id; 281 if (langpack != null && childPack.id != null && !childPack.id.equals("")) 282 { 283 childName = langpack.getString(key); 284 } 285 if (childName.equals("") || key.equals(childName)) 286 { 287 childName = childPack.name; 288 } 289 // End internationalization 290 list += childName; 291 if (j != dep.size() - 1) list += ", "; 292 } 293 dependencyArea.setText(list); 294 } 295 } 296 297 /** 298 * Layout helper method:<br> 299 * Creates an label with a message given by msgId and an icon given by the iconId. If layout and 300 * constraints are not null, the label will be added to layout with the given constraints. The 301 * label will be added to this object. 302 * 303 * @param msgId identifier for the IzPack langpack 304 * @param iconId identifier for the IzPack icons 305 * @param layout layout to be used 306 * @param constraints constraints to be used 307 * @return the created label 308 */ 309 protected JLabel createLabel(String msgId, String iconId, GridBagLayout layout, 310 GridBagConstraints constraints) 311 { 312 JLabel label = LabelFactory.create(parent.langpack.getString(msgId), parent.icons 313 .getImageIcon(iconId), JLabel.TRAILING); 314 if (layout != null && constraints != null) layout.addLayoutComponent(label, constraints); 315 add(label); 316 return (label); 317 } 318 319 /** 320 * Creates a panel containing a anonymous label on the left with the message for the given msgId 321 * and a label on the right side with initial no text. The right label will be returned. If 322 * layout and constraints are not null, the label will be added to layout with the given 323 * constraints. The panel will be added to this object. 324 * 325 * @param msgId identifier for the IzPack langpack 326 * @param layout layout to be used 327 * @param constraints constraints to be used 328 * @return the created (right) label 329 */ 330 protected JLabel createPanelWithLabel(String msgId, GridBagLayout layout, 331 GridBagConstraints constraints) 332 { 333 JPanel panel = new JPanel(); 334 JLabel label = new JLabel(); 335 if (label == null) label = new JLabel(""); 336 panel.setAlignmentX(LEFT_ALIGNMENT); 337 panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); 338 panel.add(LabelFactory.create(parent.langpack.getString(msgId))); 339 panel.add(Box.createHorizontalGlue()); 340 panel.add(label); 341 if (layout != null && constraints != null) layout.addLayoutComponent(panel, constraints); 342 add(panel); 343 return (label); 344 } 345 346 /** 347 * Creates a text area with standard settings and the title given by the msgId. If scroller is 348 * not null, the create text area will be added to the scroller and the scroller to this object, 349 * else the text area will be added directly to this object. If layout and constraints are not 350 * null, the text area or scroller will be added to layout with the given constraints. The text 351 * area will be returned. 352 * 353 * @param msgId identifier for the IzPack langpack 354 * @param scroller the scroller to be used 355 * @param layout layout to be used 356 * @param constraints constraints to be used 357 * @return the created text area 358 */ 359 protected JTextArea createTextArea(String msgId, JScrollPane scroller, GridBagLayout layout, 360 GridBagConstraints constraints) 361 { 362 JTextArea area = new JTextArea(); 363 area.setMargin(new Insets(2, 2, 2, 2)); 364 area.setAlignmentX(LEFT_ALIGNMENT); 365 area.setCaretPosition(0); 366 area.setEditable(false); 367 area.setEditable(false); 368 area.setOpaque(false); 369 area.setLineWrap(true); 370 area.setWrapStyleWord(true); 371 area.setBorder(BorderFactory.createTitledBorder(parent.langpack.getString(msgId))); 372 373 if (layout != null && constraints != null) 374 { 375 if (scroller != null) 376 { 377 layout.addLayoutComponent(scroller, constraints); 378 } 379 else 380 layout.addLayoutComponent(area, constraints); 381 } 382 if (scroller != null) 383 { 384 scroller.setViewportView(area); 385 add(scroller); 386 } 387 else 388 add(area); 389 return (area); 390 391 } 392 393 /** 394 * Creates the table for the packs. All parameters are required. The table will be returned. 395 * 396 * @param width of the table 397 * @param scroller the scroller to be used 398 * @param layout layout to be used 399 * @param constraints constraints to be used 400 * @return the created table 401 */ 402 protected JTable createPacksTable(int width, JScrollPane scroller, GridBagLayout layout, 403 GridBagConstraints constraints) 404 { 405 406 JTable table = new JTable(); 407 table.setBorder(BorderFactory.createEmptyBorder(0, 2, 0, 2)); 408 table.setIntercellSpacing(new Dimension(0, 0)); 409 table.setBackground(Color.white); 410 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 411 table.getSelectionModel().addListSelectionListener(this); 412 table.setShowGrid(false); 413 scroller.setViewportView(table); 414 scroller.setAlignmentX(LEFT_ALIGNMENT); 415 scroller.getViewport().setBackground(Color.white); 416 scroller.setPreferredSize(new Dimension(width, (idata.guiPrefs.height / 3 + 30))); 417 418 if (layout != null && constraints != null) 419 layout.addLayoutComponent(scroller, constraints); 420 add(scroller); 421 return (table); 422 } 423 424 /** 425 * Computes pack related data like the names or the dependencies state. 426 * 427 * @param packs 428 */ 429 private void computePacks(List packs) 430 { 431 names = new HashMap(); 432 dependenciesExist = false; 433 for (int i = 0; i < packs.size(); i++) 434 { 435 Pack pack = (Pack) packs.get(i); 436 names.put(pack.name, pack); 437 if (pack.dependencies != null) dependenciesExist = true; 438 } 439 } 440 441 /** 442 * Called when the panel becomes active. If a derived class implements this method also, it is 443 * recomanded to call this method with the super operator first. 444 * 445 */ 446 public void panelActivate() 447 { 448 try 449 { 450 packsTable.setModel(new PacksModel(idata.availablePacks, idata.selectedPacks, this)); 451 CheckBoxEditorRenderer packSelectedRenderer = new CheckBoxEditorRenderer(false); 452 packsTable.getColumnModel().getColumn(0).setCellRenderer(packSelectedRenderer); 453 CheckBoxEditorRenderer packSelectedEditor = new CheckBoxEditorRenderer(true); 454 packsTable.getColumnModel().getColumn(0).setCellEditor(packSelectedEditor); 455 packsTable.getColumnModel().getColumn(0).setMaxWidth(40); 456 DefaultTableCellRenderer renderer1 = new DefaultTableCellRenderer() { 457 458 /** 459 * 460 */ 461 private static final long serialVersionUID = 3256438101604710708L; 462 463 public void setBorder(Border b) 464 { 465 } 466 }; 467 packsTable.getColumnModel().getColumn(1).setCellRenderer(renderer1); 468 DefaultTableCellRenderer renderer2 = new DefaultTableCellRenderer() { 469 470 /** 471 * 472 */ 473 private static final long serialVersionUID = 4121128130480976185L; 474 475 public void setBorder(Border b) 476 { 477 } 478 479 // public void setFont(Font f) 480 // { 481 // super.setFont(new Font("Monospaced",Font.PLAIN,11)); 482 // } 483 }; 484 renderer2.setHorizontalAlignment(JLabel.RIGHT); 485 packsTable.getColumnModel().getColumn(2).setCellRenderer(renderer2); 486 packsTable.getColumnModel().getColumn(2).setMaxWidth(100); 487 488 // remove header,so we don't need more strings 489 tableScroller.remove(packsTable.getTableHeader()); 490 tableScroller.setColumnHeaderView(null); 491 tableScroller.setColumnHeader(null); 492 493 // set the JCheckBoxes to the currently selected panels. The 494 // selection might have changed in another panel 495 java.util.Iterator iter = idata.availablePacks.iterator(); 496 bytes = 0; 497 while (iter.hasNext()) 498 { 499 Pack p = (Pack) iter.next(); 500 if (p.required) 501 { 502 bytes += p.nbytes; 503 continue; 504 } 505 if (idata.selectedPacks.contains(p)) bytes += p.nbytes; 506 } 507 } 508 catch (Exception e) 509 { 510 e.printStackTrace(); 511 } 512 showSpaceRequired(); 513 showFreeSpace(); 514 } 515 516 /* 517 * (non-Javadoc) 518 * 519 * @see com.izforge.izpack.installer.IzPanel#getSummaryBody() 520 */ 521 public String getSummaryBody() 522 { 523 StringBuffer retval = new StringBuffer(256); 524 Iterator iter = idata.selectedPacks.iterator(); 525 boolean first = true; 526 while (iter.hasNext()) 527 { 528 if (!first) 529 { 530 retval.append("<br>"); 531 } 532 first = false; 533 Pack pack = (Pack) iter.next(); 534 if (langpack != null && pack.id != null && !pack.id.equals("")) 535 { 536 retval.append(langpack.getString(pack.id)); 537 } 538 else 539 retval.append(pack.name); 540 } 541 return (retval.toString()); 542 } 543 544 static class CheckBoxEditorRenderer extends AbstractCellEditor implements TableCellRenderer, 545 TableCellEditor, ActionListener 546 { 547 548 /** 549 * 550 */ 551 private static final long serialVersionUID = 4049072731222061879L; 552 553 private JCheckBox display; 554 555 public CheckBoxEditorRenderer(boolean useAsEditor) 556 { 557 display = new JCheckBox(); 558 display.setHorizontalAlignment(JLabel.CENTER); 559 if (useAsEditor) display.addActionListener(this); 560 561 } 562 563 public Component getTableCellRendererComponent(JTable table, Object value, 564 boolean isSelected, boolean hasFocus, int row, int column) 565 { 566 if (isSelected) 567 { 568 display.setForeground(table.getSelectionForeground()); 569 display.setBackground(table.getSelectionBackground()); 570 } 571 else 572 { 573 display.setForeground(table.getForeground()); 574 display.setBackground(table.getBackground()); 575 } 576 int state = ((Integer) value).intValue(); 577 display.setSelected((value != null && Math.abs(state) == 1)); 578 display.setEnabled(state >= 0); 579 return display; 580 } 581 582 /** 583 * @see javax.swing.table.TableCellEditor#getTableCellEditorComponent(javax.swing.JTable, 584 * java.lang.Object, boolean, int, int) 585 */ 586 public Component getTableCellEditorComponent(JTable table, Object value, 587 boolean isSelected, int row, int column) 588 { 589 return getTableCellRendererComponent(table, value, isSelected, false, row, column); 590 } 591 592 public Object getCellEditorValue() 593 { 594 return new Integer(display.isSelected() ? 1 : 0); 595 } 596 597 public void actionPerformed(ActionEvent e) 598 { 599 stopCellEditing(); 600 } 601 } 602 603}