/** Adds a new asset to the list. This updates the UI as necessary */
  private void addDownloadingAsset(final Asset asset) {
    // Adds a downloading asset to the list using a swing worker for thread
    // safeness
    final DownloadingAsset da = new DownloadingAsset();
    da.asset = asset;
    da.readBytes = 0;
    da.percentage = 0;
    final String assetURIString = asset.getAssetURI().toExternalForm();
    downloadingAssetMap.put(assetURIString, da);

    SwingUtilities.invokeLater(
        new Runnable() {

          public void run() {
            // set up the indicator in the AWT event thread
            da.indicator = new AssetIndicator();
            da.indicator.setAssetProgressValue(0);
            da.indicator.setAssetLabel(assetURIString);

            listModel.addElementToTail(da.indicator);
          }
        });

    updateCombined();
  }
 public void downloadProgress(Asset asset, int readBytes, int percentage) {
   // Check to see if the asset is already in the list, otherwise
   // add it. We need to synchronized according to the asset to make
   // sure we do not add it twice
   synchronized (this) {
     if (downloadingAssetMap.containsKey(asset.getAssetURI().toExternalForm()) == false) {
       addDownloadingAsset(asset);
     } else {
       updateDownloadingAsset(asset, readBytes, percentage);
     }
   }
 }
  /** Removes a downloading asset from the list and updates the UI as necessary. */
  private void removeDownloadingAsset(Asset asset) {
    // Removes a downloading asset from the list using a swing worker for
    // thread safeness
    final DownloadingAsset da = downloadingAssetMap.remove(asset.getAssetURI().toExternalForm());

    new Timer(
            750,
            new ActionListener() {

              public void actionPerformed(ActionEvent e) {
                listModel.removeElement(da.indicator);
                ((Timer) e.getSource()).stop();
              }
            })
        .start();

    updateCombined();
  }