1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.sdkuilib.internal.repository; 18 19 import com.android.sdklib.AndroidVersion; 20 import com.android.sdklib.SdkConstants; 21 import com.android.sdklib.internal.repository.Archive; 22 import com.android.sdklib.internal.repository.IPackageVersion; 23 import com.android.sdklib.internal.repository.Package; 24 import com.android.sdklib.internal.repository.SdkSource; 25 import com.android.sdkuilib.internal.repository.icons.ImageFactory; 26 import com.android.sdkuilib.ui.GridDialog; 27 28 import org.eclipse.jface.dialogs.IDialogConstants; 29 import org.eclipse.jface.viewers.ISelection; 30 import org.eclipse.jface.viewers.IStructuredContentProvider; 31 import org.eclipse.jface.viewers.IStructuredSelection; 32 import org.eclipse.jface.viewers.LabelProvider; 33 import org.eclipse.jface.viewers.TableViewer; 34 import org.eclipse.jface.viewers.Viewer; 35 import org.eclipse.jface.window.Window; 36 import org.eclipse.swt.SWT; 37 import org.eclipse.swt.custom.SashForm; 38 import org.eclipse.swt.custom.StyleRange; 39 import org.eclipse.swt.custom.StyledText; 40 import org.eclipse.swt.events.ControlAdapter; 41 import org.eclipse.swt.events.ControlEvent; 42 import org.eclipse.swt.events.SelectionAdapter; 43 import org.eclipse.swt.events.SelectionEvent; 44 import org.eclipse.swt.graphics.Image; 45 import org.eclipse.swt.graphics.Point; 46 import org.eclipse.swt.graphics.Rectangle; 47 import org.eclipse.swt.layout.GridData; 48 import org.eclipse.swt.layout.GridLayout; 49 import org.eclipse.swt.widgets.Button; 50 import org.eclipse.swt.widgets.Composite; 51 import org.eclipse.swt.widgets.Control; 52 import org.eclipse.swt.widgets.Group; 53 import org.eclipse.swt.widgets.Label; 54 import org.eclipse.swt.widgets.Shell; 55 import org.eclipse.swt.widgets.Table; 56 import org.eclipse.swt.widgets.TableColumn; 57 58 import java.util.ArrayList; 59 import java.util.Collection; 60 61 62 /** 63 * Implements an {@link SdkUpdaterChooserDialog}. 64 */ 65 final class SdkUpdaterChooserDialog extends GridDialog { 66 67 /** Last dialog size for this session. */ 68 private static Point sLastSize; 69 private boolean mLicenseAcceptAll; 70 private boolean mInternalLicenseRadioUpdate; 71 72 // UI fields 73 private SashForm mSashForm; 74 private Composite mPackageRootComposite; 75 private TableViewer mTableViewPackage; 76 private Table mTablePackage; 77 private TableColumn mTableColum; 78 private StyledText mPackageText; 79 private Button mLicenseRadioAccept; 80 private Button mLicenseRadioReject; 81 private Button mLicenseRadioAcceptAll; 82 private Group mPackageTextGroup; 83 private final UpdaterData mUpdaterData; 84 private Group mTableGroup; 85 private Label mErrorLabel; 86 87 /** 88 * List of all archives to be installed with dependency information. 89 * <p/> 90 * Note: in a lot of cases, we need to find the archive info for a given archive. This 91 * is currently done using a simple linear search, which is fine since we only have a very 92 * limited number of archives to deal with (e.g. < 10 now). We might want to revisit 93 * this later if it becomes an issue. Right now just do the simple thing. 94 *<p/> 95 * Typically we could add a map Archive=>ArchiveInfo later. 96 */ 97 private final Collection<ArchiveInfo> mArchives; 98 99 100 101 /** 102 * Create the dialog. 103 * @param parentShell The shell to use, typically updaterData.getWindowShell() 104 * @param updaterData The updater data 105 * @param archives The archives to be installed 106 */ 107 public SdkUpdaterChooserDialog(Shell parentShell, 108 UpdaterData updaterData, 109 Collection<ArchiveInfo> archives) { 110 super(parentShell, 3, false/*makeColumnsEqual*/); 111 mUpdaterData = updaterData; 112 mArchives = archives; 113 } 114 115 @Override 116 protected boolean isResizable() { 117 return true; 118 } 119 120 /** 121 * Returns the results, i.e. the list of selected new archives to install. 122 * This is similar to the {@link ArchiveInfo} list instance given to the constructor 123 * except only accepted archives are present. 124 * 125 * An empty list is returned if cancel was choosen. 126 */ 127 public ArrayList<ArchiveInfo> getResult() { 128 ArrayList<ArchiveInfo> ais = new ArrayList<ArchiveInfo>(); 129 130 if (getReturnCode() == Window.OK) { 131 for (ArchiveInfo ai : mArchives) { 132 if (ai.isAccepted()) { 133 ais.add(ai); 134 } 135 } 136 } 137 138 return ais; 139 } 140 141 /** 142 * Create the main content of the dialog. 143 * See also {@link #createButtonBar(Composite)} below. 144 */ 145 @Override 146 public void createDialogContent(Composite parent) { 147 // Sash form 148 mSashForm = new SashForm(parent, SWT.NONE); 149 mSashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1)); 150 151 152 // Left part of Sash Form 153 154 mTableGroup = new Group(mSashForm, SWT.NONE); 155 mTableGroup.setText("Packages"); 156 mTableGroup.setLayout(new GridLayout(1, false/*makeColumnsEqual*/)); 157 158 mTableViewPackage = new TableViewer(mTableGroup, SWT.BORDER | SWT.V_SCROLL | SWT.SINGLE); 159 mTablePackage = mTableViewPackage.getTable(); 160 mTablePackage.setHeaderVisible(false); 161 mTablePackage.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); 162 163 mTablePackage.addSelectionListener(new SelectionAdapter() { 164 @Override 165 public void widgetSelected(SelectionEvent e) { 166 onPackageSelected(); //$hide$ 167 } 168 @Override 169 public void widgetDefaultSelected(SelectionEvent e) { 170 onPackageDoubleClick(); 171 } 172 }); 173 174 mTableColum = new TableColumn(mTablePackage, SWT.NONE); 175 mTableColum.setWidth(100); 176 mTableColum.setText("Packages"); 177 178 179 // Right part of Sash form 180 mPackageRootComposite = new Composite(mSashForm, SWT.NONE); 181 mPackageRootComposite.setLayout(new GridLayout(4, false/*makeColumnsEqual*/)); 182 mPackageRootComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); 183 184 mPackageTextGroup = new Group(mPackageRootComposite, SWT.NONE); 185 mPackageTextGroup.setText("Package Description && License"); 186 mPackageTextGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 4, 1)); 187 mPackageTextGroup.setLayout(new GridLayout(1, false/*makeColumnsEqual*/)); 188 189 mPackageText = new StyledText(mPackageTextGroup, 190 SWT.MULTI | SWT.READ_ONLY | SWT.WRAP | SWT.V_SCROLL); 191 mPackageText.setBackground( 192 getParentShell().getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND)); 193 mPackageText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); 194 195 mLicenseRadioAccept = new Button(mPackageRootComposite, SWT.RADIO); 196 mLicenseRadioAccept.setText("Accept"); 197 mLicenseRadioAccept.addSelectionListener(new SelectionAdapter() { 198 @Override 199 public void widgetSelected(SelectionEvent e) { 200 onLicenseRadioSelected(); 201 } 202 }); 203 204 mLicenseRadioReject = new Button(mPackageRootComposite, SWT.RADIO); 205 mLicenseRadioReject.setText("Reject"); 206 mLicenseRadioReject.addSelectionListener(new SelectionAdapter() { 207 @Override 208 public void widgetSelected(SelectionEvent e) { 209 onLicenseRadioSelected(); 210 } 211 }); 212 213 Label placeholder = new Label(mPackageRootComposite, SWT.NONE); 214 placeholder.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1)); 215 216 mLicenseRadioAcceptAll = new Button(mPackageRootComposite, SWT.RADIO); 217 mLicenseRadioAcceptAll.setText("Accept All"); 218 mLicenseRadioAcceptAll.addSelectionListener(new SelectionAdapter() { 219 @Override 220 public void widgetSelected(SelectionEvent e) { 221 onLicenseRadioSelected(); 222 } 223 }); 224 225 mSashForm.setWeights(new int[] {200, 300}); 226 } 227 228 /** 229 * Creates and returns the contents of this dialog's button bar. 230 * <p/> 231 * This reimplements most of the code from the base class with a few exceptions: 232 * <ul> 233 * <li>Enforces 3 columns. 234 * <li>Inserts a full-width error label. 235 * <li>Inserts a help label on the left of the first button. 236 * <li>Renames the OK button into "Install" 237 * </ul> 238 */ 239 @Override 240 protected Control createButtonBar(Composite parent) { 241 Composite composite = new Composite(parent, SWT.NONE); 242 GridLayout layout = new GridLayout(); 243 layout.numColumns = 0; // this is incremented by createButton 244 layout.makeColumnsEqualWidth = false; 245 layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN); 246 layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN); 247 layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING); 248 layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING); 249 composite.setLayout(layout); 250 GridData data = new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1); 251 composite.setLayoutData(data); 252 composite.setFont(parent.getFont()); 253 254 // Error message area 255 mErrorLabel = new Label(composite, SWT.NONE); 256 mErrorLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1)); 257 258 // Label at the left of the install/cancel buttons 259 Label label = new Label(composite, SWT.NONE); 260 label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); 261 label.setText("[*] Something depends on this package"); 262 label.setEnabled(false); 263 layout.numColumns++; 264 265 // Add the ok/cancel to the button bar. 266 createButtonsForButtonBar(composite); 267 268 // the ok button should be an "install" button 269 Button button = getButton(IDialogConstants.OK_ID); 270 button.setText("Install"); 271 272 return composite; 273 } 274 275 // -- End of UI, Start of internal logic ---------- 276 // Hide everything down-below from SWT designer 277 //$hide>>$ 278 279 @Override 280 public void create() { 281 super.create(); 282 283 // set window title 284 getShell().setText("Choose Packages to Install"); 285 286 setWindowImage(); 287 288 // Automatically accept those with an empty license or no license 289 for (ArchiveInfo ai : mArchives) { 290 Archive a = ai.getNewArchive(); 291 if (a != null) { 292 String license = a.getParentPackage().getLicense(); 293 ai.setAccepted(license == null || license.trim().length() == 0); 294 } 295 } 296 297 // Fill the list with the replacement packages 298 mTableViewPackage.setLabelProvider(new NewArchivesLabelProvider()); 299 mTableViewPackage.setContentProvider(new NewArchivesContentProvider()); 300 mTableViewPackage.setInput(mArchives); 301 302 adjustColumnsWidth(); 303 304 // select first item 305 mTablePackage.select(0); 306 onPackageSelected(); 307 } 308 309 /** 310 * Creates the icon of the window shell. 311 */ 312 private void setWindowImage() { 313 String imageName = "android_icon_16.png"; //$NON-NLS-1$ 314 if (SdkConstants.currentPlatform() == SdkConstants.PLATFORM_DARWIN) { 315 imageName = "android_icon_128.png"; //$NON-NLS-1$ 316 } 317 318 if (mUpdaterData != null) { 319 ImageFactory imgFactory = mUpdaterData.getImageFactory(); 320 if (imgFactory != null) { 321 getShell().setImage(imgFactory.getImageByName(imageName)); 322 } 323 } 324 } 325 326 /** 327 * Adds a listener to adjust the columns width when the parent is resized. 328 * <p/> 329 * If we need something more fancy, we might want to use this: 330 * http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet77.java?view=co 331 */ 332 private void adjustColumnsWidth() { 333 // Add a listener to resize the column to the full width of the table 334 ControlAdapter resizer = new ControlAdapter() { 335 @Override 336 public void controlResized(ControlEvent e) { 337 Rectangle r = mTablePackage.getClientArea(); 338 mTableColum.setWidth(r.width); 339 } 340 }; 341 mTablePackage.addControlListener(resizer); 342 resizer.controlResized(null); 343 } 344 345 /** 346 * Captures the window size before closing this. 347 * @see #getInitialSize() 348 */ 349 @Override 350 public boolean close() { 351 sLastSize = getShell().getSize(); 352 return super.close(); 353 } 354 355 /** 356 * Tries to reuse the last window size during this session. 357 * <p/> 358 * Note: the alternative would be to implement {@link #getDialogBoundsSettings()} 359 * since the default {@link #getDialogBoundsStrategy()} is to persist both location 360 * and size. 361 */ 362 @Override 363 protected Point getInitialSize() { 364 if (sLastSize != null) { 365 return sLastSize; 366 } else { 367 // Arbitrary values that look good on my screen and fit on 800x600 368 return new Point(740, 370); 369 } 370 } 371 372 /** 373 * Callback invoked when a package item is selected in the list. 374 */ 375 private void onPackageSelected() { 376 ArchiveInfo ai = getSelectedArchive(); 377 displayInformation(ai); 378 displayMissingDependency(ai); 379 updateLicenceRadios(ai); 380 } 381 382 /** Returns the currently selected {@link ArchiveInfo} or null. */ 383 private ArchiveInfo getSelectedArchive() { 384 ISelection sel = mTableViewPackage.getSelection(); 385 if (sel instanceof IStructuredSelection) { 386 Object elem = ((IStructuredSelection) sel).getFirstElement(); 387 if (elem instanceof ArchiveInfo) { 388 return (ArchiveInfo) elem; 389 } 390 } 391 return null; 392 } 393 394 /** 395 * Updates the package description and license text depending on the selected package. 396 * <p/> 397 * Note that right now there is no logic to support more than one level of dependencies 398 * (e.g. A <- B <- C and A is disabled so C should be disabled; currently C's state depends 399 * solely on B's state). We currently don't need this. It would be straightforward to add 400 * if we had a need for it, though. This would require changes to {@link ArchiveInfo} and 401 * {@link SdkUpdaterLogic}. 402 */ 403 private void displayInformation(ArchiveInfo ai) { 404 if (ai == null) { 405 mPackageText.setText("Please select a package."); 406 return; 407 } 408 409 Archive aNew = ai.getNewArchive(); 410 if (aNew == null) { 411 // Only missing archives have a null archive, so we shouldn't be here. 412 return; 413 } 414 415 Package pNew = aNew.getParentPackage(); 416 417 mPackageText.setText(""); //$NON-NLS-1$ 418 419 addSectionTitle("Package Description\n"); 420 addText(pNew.getLongDescription(), "\n\n"); //$NON-NLS-1$ 421 422 Archive aOld = ai.getReplaced(); 423 if (aOld != null) { 424 Package pOld = aOld.getParentPackage(); 425 426 int rOld = pOld.getRevision(); 427 int rNew = pNew.getRevision(); 428 429 boolean showRev = true; 430 431 if (pNew instanceof IPackageVersion && pOld instanceof IPackageVersion) { 432 AndroidVersion vOld = ((IPackageVersion) pOld).getVersion(); 433 AndroidVersion vNew = ((IPackageVersion) pNew).getVersion(); 434 435 if (!vOld.equals(vNew)) { 436 // Versions are different, so indicate more than just the revision. 437 addText(String.format("This update will replace API %1$s revision %2$d with API %3$s revision %4$d.\n\n", 438 vOld.getApiString(), rOld, 439 vNew.getApiString(), rNew)); 440 showRev = false; 441 } 442 } 443 444 if (showRev) { 445 addText(String.format("This update will replace revision %1$d with revision %2$d.\n\n", 446 rOld, 447 rNew)); 448 } 449 } 450 451 ArchiveInfo[] aDeps = ai.getDependsOn(); 452 if ((aDeps != null && aDeps.length > 0) || ai.isDependencyFor()) { 453 addSectionTitle("Dependencies\n"); 454 455 if (aDeps != null && aDeps.length > 0) { 456 addText("Installing this package also requires installing:"); 457 for (ArchiveInfo aDep : aDeps) { 458 addText(String.format("\n- %1$s", 459 aDep.getShortDescription())); 460 } 461 addText("\n\n"); 462 } 463 464 if (ai.isDependencyFor()) { 465 addText("This package is a dependency for:"); 466 for (ArchiveInfo ai2 : ai.getDependenciesFor()) { 467 addText(String.format("\n- %1$s", 468 ai2.getShortDescription())); 469 } 470 addText("\n\n"); 471 } 472 } 473 474 addSectionTitle("Archive Description\n"); 475 addText(aNew.getLongDescription(), "\n\n"); //$NON-NLS-1$ 476 477 String license = pNew.getLicense(); 478 if (license != null) { 479 addSectionTitle("License\n"); 480 addText(license.trim(), "\n\n"); //$NON-NLS-1$ 481 } 482 483 addSectionTitle("Site\n"); 484 SdkSource source = pNew.getParentSource(); 485 if (source != null) { 486 addText(source.getShortDescription()); 487 } 488 } 489 490 /** 491 * Computes and displays missing dependencies. 492 * 493 * If there's a selected package, check the dependency for that one. 494 * Otherwise display the first missing dependency of any other package. 495 */ 496 private void displayMissingDependency(ArchiveInfo ai) { 497 String error = null; 498 499 try { 500 if (ai != null) { 501 if (ai.isAccepted()) { 502 // Case where this package is accepted but blocked by another non-accepted one 503 ArchiveInfo[] adeps = ai.getDependsOn(); 504 if (adeps != null) { 505 for (ArchiveInfo adep : adeps) { 506 if (!adep.isAccepted()) { 507 error = String.format("This package depends on '%1$s'.", 508 adep.getShortDescription()); 509 return; 510 } 511 } 512 } 513 } else { 514 // Case where this package blocks another one when not accepted 515 for (ArchiveInfo adep : ai.getDependenciesFor()) { 516 // It only matters if the blocked one is accepted 517 if (adep.isAccepted()) { 518 error = String.format("Package '%1$s' depends on this one.", 519 adep.getShortDescription()); 520 return; 521 } 522 } 523 } 524 } 525 526 // If there is no missing dependency on the current selection, 527 // just find the first missing dependency of any other package. 528 for (ArchiveInfo ai2 : mArchives) { 529 if (ai2 == ai) { 530 // We already processed that one above. 531 continue; 532 } 533 if (ai2.isAccepted()) { 534 // The user requested to install this package. 535 // Check if all its dependencies are met. 536 ArchiveInfo[] adeps = ai2.getDependsOn(); 537 if (adeps != null) { 538 for (ArchiveInfo adep : adeps) { 539 if (!adep.isAccepted()) { 540 error = String.format("Package '%1$s' depends on '%2$s'", 541 ai2.getShortDescription(), 542 adep.getShortDescription()); 543 return; 544 } 545 } 546 } 547 } else { 548 // The user did not request to install this package. 549 // Check whether this package blocks another one when not accepted. 550 for (ArchiveInfo adep : ai2.getDependenciesFor()) { 551 // It only matters if the blocked one is accepted 552 // or if it's a local archive that is already installed (these 553 // are marked as implicitly accepted, so it's the same test.) 554 if (adep.isAccepted()) { 555 error = String.format("Package '%1$s' depends on '%2$s'", 556 adep.getShortDescription(), 557 ai2.getShortDescription()); 558 return; 559 } 560 } 561 } 562 } 563 } finally { 564 mErrorLabel.setText(error == null ? "" : error); //$NON-NLS-1$ 565 } 566 } 567 568 private void addText(String...string) { 569 for (String s : string) { 570 mPackageText.append(s); 571 } 572 } 573 574 private void addSectionTitle(String string) { 575 String s = mPackageText.getText(); 576 int start = (s == null ? 0 : s.length()); 577 mPackageText.append(string); 578 579 StyleRange sr = new StyleRange(); 580 sr.start = start; 581 sr.length = string.length(); 582 sr.fontStyle = SWT.BOLD; 583 sr.underline = true; 584 mPackageText.setStyleRange(sr); 585 } 586 587 private void updateLicenceRadios(ArchiveInfo ai) { 588 if (mInternalLicenseRadioUpdate) { 589 return; 590 } 591 mInternalLicenseRadioUpdate = true; 592 593 boolean oneAccepted = false; 594 595 if (mLicenseAcceptAll) { 596 mLicenseRadioAcceptAll.setSelection(true); 597 mLicenseRadioAccept.setEnabled(true); 598 mLicenseRadioReject.setEnabled(true); 599 mLicenseRadioAccept.setSelection(false); 600 mLicenseRadioReject.setSelection(false); 601 } else { 602 mLicenseRadioAcceptAll.setSelection(false); 603 oneAccepted = ai != null && ai.isAccepted(); 604 mLicenseRadioAccept.setEnabled(ai != null); 605 mLicenseRadioReject.setEnabled(ai != null); 606 mLicenseRadioAccept.setSelection(oneAccepted); 607 mLicenseRadioReject.setSelection(ai != null && ai.isRejected()); 608 } 609 610 // The install button is enabled if there's at least one package accepted. 611 // If the current one isn't, look for another one. 612 boolean missing = mErrorLabel.getText() != null && mErrorLabel.getText().length() > 0; 613 if (!missing && !oneAccepted) { 614 for(ArchiveInfo ai2 : mArchives) { 615 if (ai2.isAccepted()) { 616 oneAccepted = true; 617 break; 618 } 619 } 620 } 621 622 getButton(IDialogConstants.OK_ID).setEnabled(!missing && oneAccepted); 623 624 mInternalLicenseRadioUpdate = false; 625 } 626 627 /** 628 * Callback invoked when one of the radio license buttons is selected. 629 * 630 * - accept/refuse: toggle, update item checkbox 631 * - accept all: set accept-all, check all items 632 */ 633 private void onLicenseRadioSelected() { 634 if (mInternalLicenseRadioUpdate) { 635 return; 636 } 637 mInternalLicenseRadioUpdate = true; 638 639 ArchiveInfo ai = getSelectedArchive(); 640 641 if (ai == null) { 642 // Should never happen. 643 return; 644 } 645 646 boolean needUpdate = true; 647 648 if (!mLicenseAcceptAll && mLicenseRadioAcceptAll.getSelection()) { 649 // Accept all has been switched on. Mark all packages as accepted 650 mLicenseAcceptAll = true; 651 for(ArchiveInfo ai2 : mArchives) { 652 ai2.setAccepted(true); 653 ai2.setRejected(false); 654 } 655 656 } else if (mLicenseRadioAccept.getSelection()) { 657 // Accept only this one 658 mLicenseAcceptAll = false; 659 ai.setAccepted(true); 660 ai.setRejected(false); 661 662 } else if (mLicenseRadioReject.getSelection()) { 663 // Reject only this one 664 mLicenseAcceptAll = false; 665 ai.setAccepted(false); 666 ai.setRejected(true); 667 668 } else { 669 needUpdate = false; 670 } 671 672 mInternalLicenseRadioUpdate = false; 673 674 if (needUpdate) { 675 if (mLicenseAcceptAll) { 676 mTableViewPackage.refresh(); 677 } else { 678 mTableViewPackage.refresh(ai); 679 } 680 displayMissingDependency(ai); 681 updateLicenceRadios(ai); 682 } 683 } 684 685 /** 686 * Callback invoked when a package item is double-clicked in the list. 687 */ 688 private void onPackageDoubleClick() { 689 ArchiveInfo ai = getSelectedArchive(); 690 691 if (ai == null) { 692 // Should never happen. 693 return; 694 } 695 696 boolean wasAccepted = ai.isAccepted(); 697 ai.setAccepted(!wasAccepted); 698 ai.setRejected(wasAccepted); 699 700 // update state 701 mLicenseAcceptAll = false; 702 mTableViewPackage.refresh(ai); 703 displayMissingDependency(ai); 704 updateLicenceRadios(ai); 705 } 706 707 private class NewArchivesLabelProvider extends LabelProvider { 708 @Override 709 public Image getImage(Object element) { 710 assert element instanceof ArchiveInfo; 711 ArchiveInfo ai = (ArchiveInfo) element; 712 713 ImageFactory imgFactory = mUpdaterData.getImageFactory(); 714 if (imgFactory != null) { 715 if (ai.isAccepted()) { 716 return imgFactory.getImageByName("accept_icon16.png"); 717 } else if (ai.isRejected()) { 718 return imgFactory.getImageByName("reject_icon16.png"); 719 } 720 return imgFactory.getImageByName("unknown_icon16.png"); 721 } 722 return super.getImage(element); 723 } 724 725 @Override 726 public String getText(Object element) { 727 assert element instanceof ArchiveInfo; 728 ArchiveInfo ai = (ArchiveInfo) element; 729 730 String desc = ai.getShortDescription(); 731 732 if (ai.isDependencyFor()) { 733 desc += " [*]"; 734 } 735 736 return desc; 737 } 738 } 739 740 private class NewArchivesContentProvider implements IStructuredContentProvider { 741 742 public void dispose() { 743 // pass 744 } 745 746 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { 747 // Ignore. The input is always mArchives 748 } 749 750 public Object[] getElements(Object inputElement) { 751 return mArchives.toArray(); 752 } 753 } 754 755 // End of hiding from SWT Designer 756 //$hide<<$ 757 } 758