Home | History | Annotate | Download | only in repository
      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