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