Package org.knopflerfish.bundle.desktop.swing

Source Code of org.knopflerfish.bundle.desktop.swing.Desktop$SplitAction

/*
* Copyright (c) 2003-2011, KNOPFLERFISH project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
*   notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above
*   copyright notice, this list of conditions and the following
*   disclaimer in the documentation and/or other materials
*   provided with the distribution.
*
* - Neither the name of the KNOPFLERFISH project nor the names of its
*   contributors may be used to endorse or promote products derived
*   from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package org.knopflerfish.bundle.desktop.swing;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import javax.swing.AbstractButton;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ButtonGroup;
import javax.swing.DefaultComboBoxModel;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextPane;
import javax.swing.JToolBar;
import javax.swing.JViewport;
import javax.swing.KeyStroke;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import java.util.prefs.Preferences;
import javax.swing.ToolTipManager;

import org.knopflerfish.bundle.desktop.swing.console.ConsoleSwing;
import org.knopflerfish.service.desktop.BundleSelectionListener;
import org.knopflerfish.service.desktop.BundleSelectionModel;
import org.knopflerfish.service.desktop.DefaultBundleSelectionModel;
import org.knopflerfish.service.desktop.SwingBundleDisplayer;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleException;
import org.osgi.framework.BundleListener;
import org.osgi.framework.Constants;
import org.osgi.framework.FrameworkEvent;
import org.osgi.framework.FrameworkListener;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.Version;
import org.osgi.service.packageadmin.PackageAdmin;
import org.osgi.service.startlevel.StartLevel;
import org.osgi.util.tracker.ServiceTracker;

/**
* The big one. This class displays the main desktop frame, menues, console
* and listens for all registered
* <tt>SwingBundleDisplayer</tt> services. These are used to create
* JComponents, which are attached to the main panels.
*/
public class Desktop
  implements
    BundleListener,
    FrameworkListener,
    DropTargetListener,
    BundleSelectionListener
{

  PackageManager   pm;
  JFrame           frame;

  Container        contentPane;

  boolean alive = false;

  JCardPane          bundlePanel;
  JTabbedPane        detailPanel;
  JTabbedPane        consolePanel;

  final static ImageIcon emptyIcon
    = new ImageIcon(Desktop.class.getResource("/empty.gif"));
  final static Icon          updateIcon
    = new ImageIcon(Desktop.class.getResource("/update.png"));
  final static Icon startIcon
    = new ImageIcon(Desktop.class.getResource("/player_play.png"));
  final static Icon          stopIcon
    = new ImageIcon(Desktop.class.getResource("/player_stop.png"));

  final static ImageIcon uninstallIcon
    = new ImageIcon(Desktop.class.getResource("/player_eject.png"));
  final static ImageIcon installIcon
    = new ImageIcon(Desktop.class.getResource("/player_install.png"));

  /* REMOVED
    final static ImageIcon magPlusIcon
    = new ImageIcon(Desktop.class.getResource("/viewmag+.png"));
  final static ImageIcon magMinusIcon
    = new ImageIcon(Desktop.class.getResource("/viewmag-.png"));
  final static ImageIcon magFitIcon
    = new ImageIcon(Desktop.class.getResource("/viewmagfit.png"));
  final static ImageIcon mag1to1Icon
    = new ImageIcon(Desktop.class.getResource("/viewmag1.png"));
  */
  // final static ImageIcon reloadIcon
  // = new ImageIcon(Desktop.class.getResource("/reload_green.png"));

  final static ImageIcon arrowUpIcon
    = new ImageIcon(Desktop.class.getResource("/1uparrow.png"));

  final static ImageIcon arrowDownIcon
    = new ImageIcon(Desktop.class.getResource("/1downarrow.png"));

  final static ImageIcon arrowUp2Icon
    = new ImageIcon(Desktop.class.getResource("/2uparrow.png"));
  final static ImageIcon arrowDown2Icon
    = new ImageIcon(Desktop.class.getResource("/2downarrow.png"));

  final static ImageIcon viewIcon
    = new ImageIcon(Desktop.class.getResource("/view_select.png"));

  final static ImageIcon openIcon
    = new ImageIcon(Desktop.class.getResource("/open.png"));
  final static ImageIcon openURLIcon
    = new ImageIcon(Desktop.class.getResource("/bundle_small.png"));
  final static ImageIcon saveIcon
    = new ImageIcon(Desktop.class.getResource("/save.png"));

  final static ImageIcon prevIcon
    = new ImageIcon(Desktop.class.getResource("/player_prev.png"));
  final static ImageIcon nextIcon
    = new ImageIcon(Desktop.class.getResource("/player_next.png"));
  final static ImageIcon connectIcon
    = new ImageIcon(Desktop.class.getResource("/connect.png"));
  final static ImageIcon connectIconLarge
    = new ImageIcon(Desktop.class.getResource("/connect48x48.png"));

  final static ImageIcon tipIcon
    = new ImageIcon(Desktop.class.getResource("/idea.png"));
  final static ImageIcon floatIcon
    = new ImageIcon(Desktop.class.getResource("/float.png"));

  final static int mask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();

  JToolBar           toolBar;
  StatusBar          statusBar;
  JMenuBar           menuBar;

  JMenuItem          menuRemote;
  JButton            buttonRemote;

  public JCheckBoxMenuItem  logCheckBox = null;

  BundleSelectionModel bundleSelModel = new DefaultBundleSelectionModel();

  ListSelectionModel bundleSelection;

  ConsoleSwing       consoleSwing;

  JSplitPane          splitPane;
  JSplitPane          splitPaneHoriz;
  //  BundleInfoDisplayer displayHTML;

  LFManager lfManager;
  LookAndFeelMenu lfMenu;

  ServiceTracker dispTracker;
  ServiceTracker slTracker;
  ServiceTracker pkgTracker;

  JButton viewSelection;

  static Desktop theDesktop;

  Set sizesavers = new HashSet();

  // Check that we are on Mac OS X.  This is crucial to loading and
  // using the OSXAdapter class.
  public static boolean bMacOS = OSXAdapter.isMacOSX();

  public Desktop() {
    theDesktop = this;
  }

  /* We use a comparator to make sure that the ordering of
   * the different displays is constant. O/w we would end
   * up with a UI where menu item changes place from time
   * to time.
   */
  private Comparator referenceComparator = new Comparator() {
        public int compare(Object obj1, Object obj2) {
                ServiceReference ref1 = (ServiceReference)obj1;
                ServiceReference ref2 = (ServiceReference)obj2;
                Long l1 = (Long)ref1.getProperty(Constants.SERVICE_ID);
                Long l2 = (Long)ref2.getProperty(Constants.SERVICE_ID);
                return l1.compareTo(l2);
        }
  };

  Map displayMap = new TreeMap(referenceComparator);
  Map menuMap    = new HashMap();
  Map detailMap  = new HashMap();



  public void start() {

    slTracker =
      new ServiceTracker(Activator.getTargetBC(),
                         StartLevel.class.getName(), null);
    slTracker.open();

    pkgTracker =
      new ServiceTracker(Activator.getTargetBC(),
                         PackageAdmin.class.getName(), null);
    pkgTracker.open();


    lfManager = new LFManager();
    lfManager.init();

    consoleSwing = new ConsoleSwing(Activator.getTargetBC());
    consoleSwing.start();

    toolBar       = makeToolBar();
    statusBar     = new StatusBar("");

    String rName = Activator.remoteHost;
    String spid  = Activator.getBC().getProperty("org.osgi.provisioning.spid");

    if(spid == null) {
      spid = "";
    }


    try {
      ToolTipManager.sharedInstance().setInitialDelay(50);
    } catch (Exception e) {
      Activator.log.warn("Failed to change tooltip manager", e);
    }

    frame       = new JFrame(Strings.fmt("frame_title", rName, spid));

    frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
    frame.addWindowListener(new WindowAdapter() {
        public void windowClosing(WindowEvent e) {
          stopFramework();
        }
      });

    // If running on Mac OS, create eawt Application to catch Mac OS
    // quit and about events
    if(bMacOS) {
      try {
        OSXAdapter.setQuitHandler(this,
                                  getClass().getDeclaredMethod("stopFramework",
                                                               (Class[])null));
        OSXAdapter.setAboutHandler(this,
                                   getClass().getDeclaredMethod("showVersion",
                                                                (Class[])null));
      } catch (Exception e) {
        Activator.log.warn("Error while loading the OSXAdapter", e);
        bMacOS = false;
      }
    }

    contentPane = frame.getContentPane();
    contentPane.setLayout(new BorderLayout());

    SizeSaver ss = new SizeSaver("top", new Dimension(900, 600), -1);

    ss.attach(frame);
    sizesavers.add(ss);

    bundlePanel = new JCardPane();
    // bundlePanel.setPreferredSize(new Dimension(400, 300));

    toolBar       = makeToolBar();


    detailPanel   = new JTabbedPane();
    // detailPanel.setPreferredSize(new Dimension(400, 300));

    detailPanel.setTabPlacement(JTabbedPane.BOTTOM);

    detailPanel.setBorder(null);

    detailPanel.addChangeListener(new ChangeListener() {
        public void stateChanged(ChangeEvent e) {
          for(Iterator it = detailMap.keySet().iterator(); it.hasNext();) {
            ServiceReference sr = (ServiceReference)it.next();
            Object obj = detailMap.get(sr);

            if(obj instanceof DefaultSwingBundleDisplayer) {

              ((DefaultSwingBundleDisplayer)obj).setTabSelected();
            }
          }
        }
      });

    //    displayHTML = new BundleInfoDisplayerHTML(this);


    contentPane.add(toolBar,      BorderLayout.NORTH);
    contentPane.add(statusBar,    BorderLayout.SOUTH);


    splitPaneHoriz = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
                                    bundlePanel,
                                    detailPanel);

    ss = new SizeSaver("splitPaneHoriz",
                       null, // new Dimension(700, 400),
                       350);
    ss.attach(splitPaneHoriz);
    sizesavers.add(ss);
    // splitPaneHoriz.setDividerLocation(bundlePanel.getPreferredSize().width);

    splitPaneHoriz.setOneTouchExpandable(false);


    JFloatable consoleWrapper =
      new JFloatable(consoleSwing.getJComponent(), "Console");

    splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
                               splitPaneHoriz,
                               consoleWrapper);

    ss = new SizeSaver("splitPaneVertical",
                       null, // new Dimension(800, 600),
                       300);
    ss.attach(splitPane);
    sizesavers.add(ss);

    // splitPane.setDividerLocation(300);
    splitPane.setOneTouchExpandable(false);


    contentPane.add(splitPane,  BorderLayout.CENTER);


    DropTarget dt =
      new DropTarget(contentPane,
                     DnDConstants.ACTION_COPY_OR_MOVE, // actions
                     this,
                     true);



    alive = true;

    // Catch up the set of bundles; no need to call bundleChanged for
    // each bundle since that method ignores the actual event!
    // Bundle[]  bl = Activator.getTargetBCbundles();
    // for(int i = 0; i  < bl.length; i++) {
    //   bundleChanged(new BundleEvent(BundleEvent.INSTALLED, bl[i]));
    // }
    bundleChanged((BundleEvent) null);

    pm = new PackageManager(pkgTracker);

    frame.setJMenuBar(menuBar = makeMenuBar());


    setRemote(Activator.remoteTracker.getService() != null);

    setIcon(frame, "/kf-");

    // frame.pack() not used since SizeSaver(frame) does a setSize()
    // frame.pack();
    frame.setVisible(true);
    frame.toFront();

//    String dispFilter1 =
//      "(&" +
//      "(" + Constants.OBJECTCLASS + "=" +
//      SwingBundleDisplayer.class.getName() +
//      ")" +
//      "(" + SwingBundleDisplayer.PROP_ISDETAIL + "=false" + ")" +
//      ")";

    String dispFilter =
      "(" + Constants.OBJECTCLASS + "=" +
      SwingBundleDisplayer.class.getName() +
      ")";

    try {
      dispTracker =
        new ServiceTracker(Activator.getBC(),
                           Activator.getBC().createFilter(dispFilter),
                           null)
        {
          public Object addingService(final ServiceReference sr) {
            final SwingBundleDisplayer disp =
              (SwingBundleDisplayer)super.addingService(sr);

            SwingUtilities.invokeLater(new Runnable() {
                public void run() {

                  Icon   icon = disp.getSmallIcon();

                  String  name =
                    Util.getStringProp(sr,
                                       SwingBundleDisplayer.PROP_NAME,
                                       disp.getClass().getName());
                  String  desc =
                    Util.getStringProp(sr,
                                       SwingBundleDisplayer.PROP_DESCRIPTION,
                                       "");

                  boolean bDetail    =
                    Util.getBooleanProp(sr,
                                        SwingBundleDisplayer.PROP_ISDETAIL,
                                        false);

                  JComponent comp = disp.createJComponent();

                  JFloatable wrapper = new JFloatable(comp, name);

                  // floating windows shouldn't be closed when
                  // the tabbed pane swaps components
                  wrapper.setAutoClose(false);

                  disp.setBundleSelectionModel(bundleSelModel);

                  if(bDetail) {
                    detailMap.put(sr, disp);

                    //              JPanel wrapper2 = new JPanel(new BorderLayout());
                    //              wrapper2.add(wrapper, BorderLayout.CENTER);

                    detailPanel.addTab(name, icon, wrapper, desc);
                  } else {
                    displayMap.put(sr, disp);

                    bundlePanel.addTab(name, wrapper);

                    makeViewPopupMenu();

                    viewMenu = makeViewMenu(viewMenu);
                  }

                }
              });
            return disp;
          }

          public void removedService(final ServiceReference sr,
                                     final Object service) {
            final SwingBundleDisplayer disp = (SwingBundleDisplayer)service;
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                  String  name =
                    Util.getStringProp(sr,
                                       SwingBundleDisplayer.PROP_NAME,
                                       disp.getClass().getName());
                  boolean bDetail    =
                    Util.getBooleanProp(sr,
                                        SwingBundleDisplayer.PROP_ISDETAIL,
                                        false);


                  if(bDetail) {
                    Component comp = null;
                    for(int i = 0; i < detailPanel.getTabCount(); i++) {
                      if(detailPanel.getTitleAt(i).equals(name)) {
                        comp = detailPanel.getComponentAt(i);
                      }
                    }
                    if(comp != null) {
                      // Make sure floating windows are closed
                      if(comp instanceof JFloatable) {
                        ((JFloatable)comp).setAutoClose(true);
                        ((JFloatable)comp).doUnfloat();
                      }
                      detailPanel.remove(comp);
                      detailMap.remove(sr);
                    }
                  } else {
                    Component comp = bundlePanel.getTab(name);
                    if(comp != null) {
                      if(comp instanceof JFloatable) {
                        ((JFloatable)comp).setAutoClose(true);
                        ((JFloatable)comp).doUnfloat();
                      }
                    }

                    displayMap.remove(sr);
                    bundlePanel.removeTab(name);

                    makeViewPopupMenu();
                    viewMenu = makeViewMenu(viewMenu);

                  }
                }
              });
            super.removedService(sr, service);
          }
        };
      dispTracker.open();
    } catch (Exception e) {
      Activator.log.error("Failed to create tracker", e);
    }


    bundleSelModel.addBundleSelectionListener(this);
    Activator.getTargetBC().addBundleListener(this);
    Activator.getTargetBC().addFrameworkListener(this);

    updateBundleViewSelections();
    consoleSwing.getJComponent().requestFocus();

    checkUpdate(false);
  }

  // Assumes that the current Knopflerfish version can be found
  // directly after the text "Knopflerfish " on the first line of the
  // release note at
  // http://www.knopflerfish.org/releases/current/release_notes.txt
  void checkUpdate(final boolean bForce) {
    // Run in threads, to avoid startup delay caused by network problems.
    new Thread() {
      public void run() {
        try {
          if(bForce) {
            Preferences prefs = Preferences.userNodeForPackage(getClass());
            prefs.remove(KEY_UPDATEVERSION);
            prefs.flush();
          }

          String versionURL =
            Util.getProperty("org.knopflerfish.desktop.releasenotesurl",
                             "http://www.knopflerfish.org/releases/current/release_notes.txt");

          URL url  = new URL(versionURL);
          URLConnection conn = url.openConnection();
          InputStream is = conn.getInputStream();

          String notes = new String(Util.readStream(is), "ISO-8859-1");

          int ix = notes.indexOf("\n");
          if(ix != -1) {
            String line = notes.substring(0, ix);
            final String keyWord = "Knopflerfish ";
            ix = line.lastIndexOf(keyWord);
            if(ix != -1) {
              line = line.substring(ix + keyWord.length()).trim();
              ix = line.indexOf(" ");
              if (ix!=-1) {
                line = line.substring(0, ix);
              }
              Version version = new Version(line);
              Bundle sysBundle = Activator.getBC().getBundle(0);
              Version sysVersion = new Version((String)sysBundle.getHeaders().get("Bundle-Version"));

              Activator.log.info("sysVersion=" + sysVersion
                                 +", version=" + version);
              if(sysVersion.compareTo(version) < 0) {
                showUpdate(sysVersion, version, notes);
              }
            } else {
              Activator.log.warn("No version info in " + line);
            }
          } else {
            Activator.log.warn("No version line");
          }
        } catch (Exception e) {
          Activator.log.warn("Failed to read update info", e);
        }
      }
    }.start();
  }

  final Action actionStartBundles
    = new AbstractAction(Strings.get("item_startbundles"), startIcon) {
        public void actionPerformed(ActionEvent ev) {
          startBundles(getSelectedBundles());
        }
      };
  final Action actionStopBundles
    = new AbstractAction(Strings.get("item_stopbundles"), stopIcon) {
        public void actionPerformed(ActionEvent ev) {
          stopBundles(getSelectedBundles());
        }
      };
  final Action actionUpdateBundles =
      new AbstractAction(Strings.get("item_updatebundles"), updateIcon) {
        public void actionPerformed(ActionEvent ev) {
          updateBundles(getSelectedBundles());
        }
      };

  final Action actionUninstallBundles =
      new AbstractAction(Strings.get("item_uninstallbundles"), uninstallIcon) {
        public void actionPerformed(ActionEvent ev) {
          uninstallBundles(getSelectedBundles());
        }
      };

  final Action actionRefreshBundles =
      new AbstractAction(Strings.get("menu_refreshbundles")) {
        {
          putValue(ACCELERATOR_KEY,
                   KeyStroke.getKeyStroke(KeyEvent.VK_R, mask));
          putValue(SHORT_DESCRIPTION, Strings.get("menu_refreshbundles.descr"));
        }
        public void actionPerformed(ActionEvent ev) {
          refreshBundle(getSelectedBundles());
        }
      };

  final Action actionResolveBundles =
      new AbstractAction(Strings.get("menu_resolvebundles")) {
        {
          putValue(SHORT_DESCRIPTION, Strings.get("menu_resolvebundles.descr"));
        }
        public void actionPerformed(ActionEvent ev) {
          resolveBundles(getSelectedBundles());
        }
      };


  JButton toolStartBundles;
  JButton toolStopBundles;
  JButton toolUpdateBundles;
  JButton toolUninstallBundles;

  void setRemote(boolean b) {
    menuRemote.setEnabled(b);
    buttonRemote.setEnabled(b);
  }

  JToolBar makeToolBar() {
    return new JToolBar() {
        {
          add(toolStartBundles = new JButton(openIcon) {
              {
                addActionListener(new ActionListener() {
                    public void actionPerformed(ActionEvent ev) {
                      addBundle();
                    }
                  });
                setToolTipText(Strings.get("menu_openbundles"));
              }
            });

          add(new JButton(openURLIcon) {
              {
                addActionListener(new ActionListener() {
                    public void actionPerformed(ActionEvent ev) {
                      addBundleURL();
                    }
                  });
                setToolTipText(Strings.get("menu_openbundleurl"));
              }
            });

          add(toolStartBundles = new JButton(saveIcon) {
              {
                addActionListener(new ActionListener() {
                    public void actionPerformed(ActionEvent ev) {
                      save();
                    }
                  });
                setToolTipText(Strings.get("menu_save"));
              }
            });


          add(buttonRemote = new JButton(connectIcon) {
              {
                addActionListener(new ActionListener() {
                    public void actionPerformed(ActionEvent ev) {
                      doConnect();
                    }
                  });
                setToolTipText(Strings.get("menu_remotefw"));
              }
            });
          //          add(new JToolBar.Separator());

          add(new JToolbarButton(actionStartBundles));
          add(new JToolbarButton(actionStopBundles));
          add(new JToolbarButton(actionUpdateBundles));
          add(new JToolbarButton(actionUninstallBundles));

          add(viewSelection = makeViewSelectionButton());

          StartLevel sls = (StartLevel)slTracker.getService();

          if(null == sls) {
            add(new JLabel(Strings.get("nostartlevel.label")));
          } else {
            add(makeStartLevelSelector());
            add(levelBox);

          }
        }
      };
  }

  JComponent makeStartLevelSelector() {
    StartLevel sls =
      (StartLevel)slTracker.getService();

    Activator.log.debug("has start level service");

    JPanel panel = new JPanel();

    panel.add(new JLabel(Strings.get("startlevel.label")));

    levelBox = new JComboBox();

    updateLevelItems();

    levelBox.setSelectedIndex(sls.getStartLevel() - levelMin);

    levelBox.addActionListener(new ActionListener() {

        public void actionPerformed(ActionEvent ev) {

          if(levelBox.getSelectedIndex() == -1) {
            return;
          }

          // Delay actual setting to avoid flipping thru
          // levels quickly.
          SwingUtilities.invokeLater(new Runnable() {
              public void run() {
                Thread t = new Thread() {
                  public void run() {
                    try {
                      Thread.sleep(500);
                      setFWStartLevel();
                    } catch (Exception e) {
                      Activator.log.error("Failed to set start level");
                    }
                  }
                };
                t.start();
              }
            });
        }
      });

    panel.add(levelBox);

    return panel;

  }

  void setFWStartLevel() {
    int level = levelBox.getSelectedIndex() + levelMin;

    StartLevel sls =
      (StartLevel)slTracker.getService();


    if(sls != null) {
      if(sls.getStartLevel() == level) {
        return;
      }
    }

    int myLevel = level;
    try {
      myLevel = sls.getBundleStartLevel(Activator.getTargetBC().getBundle());
    } catch (IllegalArgumentException ignored) {
    }

    boolean bOK = true;

    if(level < myLevel) {
      bOK = false;
      Object[] options = {Strings.get("yes"),
                          Strings.get("cancel")};


      int n =JOptionPane
        .showOptionDialog(frame,
                          Strings.get("q_stopdesktop"),
                          Strings.get("msg_stopdesktop"),
                          JOptionPane.YES_NO_OPTION,
                          JOptionPane.QUESTION_MESSAGE,
                          null,
                                options,
                          options[1]);
      if(n == 0) {
        bOK = true;
      }
    }
    if(bOK) {
      setStartLevel(level);
    } else {
      if(sls != null) {
        levelBox.setSelectedIndex(sls.getStartLevel() - levelMin);
      }
    }
  }

  JButton makeViewSelectionButton() {
    // view selection button
    JButton viewButton = new JButton(viewIcon);

    makeViewPopupMenu();

    viewButton.addMouseListener(new MouseAdapter()
      {
        public void mousePressed(MouseEvent e) {
          showPopup(e);
        }

        public void mouseReleased(MouseEvent e) {
          showPopup(e);
        }

        private void showPopup(MouseEvent e) {
          if (viewPopupMenu != null) {
            Component comp = e.getComponent();
            viewPopupMenu.show(comp, 0, comp.getSize().height);
          }
        }
      });

    // end view selection button

    return viewButton;
  }


  JPopupMenu viewPopupMenu;

  void makeViewPopupMenu() {

    viewPopupMenu = new JPopupMenu();
    menuMap.clear();

    for(Iterator it = displayMap.keySet().iterator(); it.hasNext(); ) {
      final ServiceReference     sr   = (ServiceReference)it.next();
      final String key = (String)sr.getProperty(SwingBundleDisplayer.PROP_NAME);

      JMenuItem item = new JMenuItem(key);
      item.addActionListener(new ActionListener() {
          public void actionPerformed(ActionEvent ev) {
            bundlePanelShowTab(sr);
          }
        });
      viewPopupMenu.add(item);
    }
  }

  void bundlePanelShowTab(String name) {
    ServiceReference[] sr;
    try {
      sr = Activator.getBC().getServiceReferences(SwingBundleDisplayer.class.getName(),
          "("+SwingBundleDisplayer.PROP_NAME+"=" + name +")");
      if (sr != null) {
        bundlePanelShowTab(sr[0]);
      }
    } catch (InvalidSyntaxException e) {
      throw new RuntimeException(e.getMessage());
    }
  }

  void bundlePanelShowTab(ServiceReference sr) {
    final String key = (String)sr.getProperty(SwingBundleDisplayer.PROP_NAME);
    bundlePanel.showTab(key);
    final JRadioButtonMenuItem item = (JRadioButtonMenuItem) menuMap.get(sr);
    if (null!=item) {
      item.setSelected(true);
    }
  }


  void updateLevelItems() {
    StartLevel sls =
      (StartLevel)slTracker.getService();

    if(sls != null) {
      levelMax = Math.max(levelMax, sls.getStartLevel());
    }
    levelItems = new String[levelMax - levelMin + 1];

    Bundle[] bundles = Activator.getTargetBC().getBundles();

    Object selObj = null;

    for(int i = levelMin; i <= levelMax; i++) {
      StringBuffer sb = new StringBuffer();
      int level = i;
      boolean bOverflow = false;
      for(int j = 0; j < bundles.length; j++) {
        try {
          if(sls != null && sls.getBundleStartLevel(bundles[j]) == level) {
            if(sb.length() > 0) {
              sb.append(", ");
            }
            String name = Util.getBundleName(bundles[j]);
            //              Text.replace(Util.shortLocation(bundles[j].getLocation()), ".jar", "");
            sb.append(name);
          }
        } catch (IllegalArgumentException e) {
        }
      }
      String txt = sb.toString();
      int maxLen = 50;
      if(txt.length() > maxLen) {
        txt = txt.substring(0, maxLen) + "...";
      }
      levelItems[i - levelMin] = i + " " + txt;

      if(i == sls.getStartLevel()) {
        selObj = levelItems[i - levelMin];
      }
    }

    if(levelBox != null) {
      DefaultComboBoxModel model = new DefaultComboBoxModel(levelItems);
      levelBox.setModel(model);
    }
  }


  void setStartLevel(final int level) {

    Thread t = new Thread() {
        public void run() {
          StartLevel sls = (StartLevel)slTracker.getService();

          if(null != sls) {
            sls.setStartLevel(level);
          }
        }
      };
    t.start();
  }

  void updateStartLevel() {
    if(slTracker == null) {
      return;
    }

    StartLevel sls = (StartLevel)slTracker.getService();

    if(sls == null) {
      return;
    }

    updateLevelItems();
    if(levelBox != null) {
      levelBox.setSelectedIndex(sls.getStartLevel() - levelMin);
    }
    updateBundleViewSelections();
  }

  // items handling start level stuff. Only used if a StartLevel
  // service is available at startup
  Object[]       levelItems;
  JComboBox      levelBox = null;
  int            levelMin = 1;
  int            levelMax = 20;


  int      baActive    = 0;
  int      baInstalled = 0;
  int      other       = 0;
  Bundle[] bl          = new Bundle[0];


  boolean stopActive() {
    return
      baActive == bl.length ||
      baActive + other == bl.length;
  }

  boolean startActive() {
    return
      baInstalled == bl.length ||
      baInstalled + other== bl.length;
  }

  public void setSelected(Bundle b) {
    bundleSelModel.clearSelection();
    ensureSelected(b);
  }

  public void ensureSelected(Bundle b) {
    bundleSelModel.setSelected(b.getBundleId(), true);
    updateStatusBar();
  }

  public void toggleSelected(Bundle b) {
    bundleSelModel.setSelected(b.getBundleId(),
                               !bundleSelModel.isSelected(b.getBundleId()));
    updateStatusBar();
  }

  public boolean isSelected(Bundle b) {
    return bundleSelModel.isSelected(b.getBundleId());
  }

  void updateBundleViewSelections() {

    Bundle[] bl = getSelectedBundles();


    //    displayHTML.selectionUpdated(bl);

    boolean bEnabled = bl.length > 0;

    actionStartBundles.setEnabled(bEnabled);
    actionStopBundles.setEnabled(bEnabled);
    actionUninstallBundles.setEnabled(bEnabled);
    actionUpdateBundles.setEnabled(bEnabled);

    toolBar.invalidate();
    menuBar.invalidate();

    if (null!=startLevelMenu) {
      startLevelMenu.setEnabled(bEnabled);
    }

    if(levelMenuLabel != null) {
      levelMenuLabel.setText(Strings.get("startlevel.noSel"));
      noStartLevelSelected.setSelected(true);
    }

    final StartLevel sls = (StartLevel) slTracker.getService();
    if (null!=sls) {
      final Set levels = new HashSet();
      final Set bids = new HashSet();
      for (int i=0; i<bl.length; i++) {
        try {
          //levels.add(new Integer(sls.getBundleStartLevel(bl[i])));
          final Integer lvl = new Integer(sls.getBundleStartLevel(bl[i]));
          levels.add(lvl);
          bids.add(new Long(bl[i].getBundleId()));
        } catch (Exception e) {
        }
      }
      levelMenuLabel.setText("Bundle " + bids);
      if (1==levels.size()) {
        final Integer level = (Integer) levels.iterator().next();
        final AbstractButton jrb = (AbstractButton) levelCheckBoxes.get(level);
        if (null!=jrb) jrb.setSelected(true);
      }
    }
  }

  int divloc = 0;

  JMenuBar makeMenuBar() {
    return new JMenuBar() {
        {
          add(makeFileMenu());
          add(editMenu = makeEditMenu());
          add(makeBundleMenu());
          add(viewMenu = makeViewMenu(null));
          add(makeHelpMenu());
        }
      };
  }

  void updateMenus() {
    if(editMenu != null) {
      editMenu.removeAll();
      updateEditMenu(editMenu);
    }
  }

  JMenu makeFileMenu() {

    return new JMenu(Strings.get("menu_file")) {
        {
          add(new JMenuItem(Strings.get("menu_openbundles"), openIcon) {
              {
                setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O,
                                                      mask));
                setMnemonic(KeyEvent.VK_O);

                addActionListener(new ActionListener() {
                    public void actionPerformed(ActionEvent ev) {
                      addBundle();
                    }
                  });
              }});
          add(new JMenuItem(Strings.get("menu_openbundleurl"), openURLIcon) {
              {
                setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_L,
                                                      mask));
                setMnemonic(KeyEvent.VK_U);

                addActionListener(new ActionListener() {
                    public void actionPerformed(ActionEvent ev) {
                      addBundleURL();
                    }
                  });
              }});
          add(new JMenuItem(Strings.get("menu_save"), saveIcon) {
              {
                setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S,
                                                      mask));
                setMnemonic(KeyEvent.VK_S);

                addActionListener(new ActionListener() {
                    public void actionPerformed(ActionEvent ev) {
                      save();
                    }
                  });
              }});

          add(menuRemote = new JMenuItem(Strings.get("menu_remotefw"), connectIcon) {
              {
                setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F,
                                                      mask));
                setMnemonic(KeyEvent.VK_F);

                addActionListener(new ActionListener() {
                    public void actionPerformed(ActionEvent ev) {
                      doConnect();
                    }
                  });
              }
            });

          if(!bMacOS) {
            add(new JMenuItem("Quit framework...") {
                {
                  setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
                                                        mask));
                  setMnemonic(KeyEvent.VK_Q);
                  addActionListener(new ActionListener() {
                      public void actionPerformed(ActionEvent ev) {
                        stopFramework();
                      }
                    });
                }});
          }
        }
      };
  }

  JMenuItem itemStopBundles;
  JMenuItem itemStartBundles;
  JMenuItem itemUpdateBundles;
  JMenuItem itemUninstallBundles;
  JMenuItem itemResolveBundles;
  JMenuItem itemRefreshBundles;
  JMenu     startLevelMenu;


  JMenu makeBundleMenu() {

    return new JMenu(Strings.get("menu_bundles")) {
        {
          add(itemStopBundles   = new JMenuItem(actionStopBundles));
          add(makeStopOptionsMenu());
          addSeparator();
          add(itemStartBundles  = new JMenuItem(actionStartBundles));
          add(makeStartOptionsMenu());
          addSeparator();
          add(itemUpdateBundles = new JMenuItem(actionUpdateBundles));
          add(itemUninstallBundles = new JMenuItem(actionUninstallBundles));
          add(itemResolveBundles   = new JMenuItem(actionResolveBundles));
          add(itemRefreshBundles   = new JMenuItem(actionRefreshBundles));

          StartLevel sls = (StartLevel)slTracker.getService();
          if(sls != null) {
            add(startLevelMenu = makeStartLevelMenu());
          }
        }
      };
  }

  // Integer -> AbstractButton
  Map    levelCheckBoxes = new HashMap();
  // Use a menu item here even though a label should suffice, but the
  // MacOSX mapping to native (AWT) menu items requires a menu item to
  // work.
  JMenuItem levelMenuLabel  = null;
  // Invisible menu item that when selected represents no selection.
  final JRadioButtonMenuItem noStartLevelSelected
    = new JRadioButtonMenuItem("no selection");

  JMenu makeStartLevelMenu() {
    return new JMenu(Strings.get("menu_startlevel")) {
        {
          setToolTipText(Strings.get("startlevel.descr"));
          final ButtonGroup group = new ButtonGroup();
          group.add(noStartLevelSelected);

          add(levelMenuLabel = new JMenuItem(Strings.get("startlevel.noSel")));
          add(new JSeparator());

          for(int i = levelMin; i <= levelMax; i++) {
            final AbstractButton jrb = new JRadioButtonMenuItem(Integer.toString(i));
            group.add(jrb);
            add(jrb);
            jrb.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent ev) {
                  final StartLevel sls = (StartLevel)slTracker.getService();

                  final Bundle[] bl = getSelectedBundles();

                  if(null != sls) {
                    final int level = Integer.parseInt(jrb.getText());
                    for (int i=0; i<bl.length; i++) {
                      sls.setBundleStartLevel(bl[i], level);
                    }
                    updateBundleViewSelections();
                  }
                }
              });

            levelCheckBoxes.put(new Integer(i), jrb);
          }
        }
      };
  }


  JCheckBoxMenuItem itemStopOptionsTransient;

  JMenu makeStopOptionsMenu() {
    return new JMenu(Strings.get("menu_stopOptions")) {
        {
          setToolTipText(Strings.get("menu_stopOptions.descr"));

          final ItemListener itemListener = new ItemListener(){
              public void itemStateChanged(ItemEvent e){
                updateNameOfActionStopBundles();
              }
            };

          itemStopOptionsTransient = new JCheckBoxMenuItem
            (Strings.get("stop_option_transient"), false);
          itemStopOptionsTransient.setToolTipText
            (Strings.get("stop_option_transient.descr"));
          itemStopOptionsTransient.addItemListener(itemListener);

          add(itemStopOptionsTransient);
          updateNameOfActionStopBundles();
        }
      };
  }

  void updateNameOfActionStopBundles()
  {
    final boolean trans = itemStopOptionsTransient.getState();
    final String name = Strings.get("item_stopbundles")
      +(trans ? " (" : "")
      +(trans ? Strings.get("stop_option_transient") : "")
      +(trans ? ")" : "");

    actionStopBundles.putValue(Action.NAME, name);
  }

  // Get stop option settings from the menu.
  int getStopOptions()
  {
    int options = 0;
    if (itemStopOptionsTransient.getState()) {
      options |= Bundle.STOP_TRANSIENT;
    }
    return options;
  }


  JCheckBoxMenuItem itemStartOptionsTransient;
  JCheckBoxMenuItem itemStartOptionsPolicy;

  JMenu makeStartOptionsMenu() {
    return new JMenu(Strings.get("menu_startOptions")) {
        {
          setToolTipText(Strings.get("menu_startOptions.descr"));

          final ItemListener itemListener = new ItemListener(){
              public void itemStateChanged(ItemEvent e){
                updateNameOfActionStartBundles();
              }
            };

          itemStartOptionsTransient = new JCheckBoxMenuItem
            (Strings.get("start_option_transient"), false);
          itemStartOptionsTransient.setToolTipText
            (Strings.get("start_option_transient.descr"));
          itemStartOptionsTransient.addItemListener(itemListener);

          itemStartOptionsPolicy = new JCheckBoxMenuItem
            (Strings.get("start_option_policy"), true);
          itemStartOptionsPolicy.setToolTipText
            (Strings.get("start_option_policy.descr"));
          itemStartOptionsPolicy.addItemListener(itemListener);

          add(itemStartOptionsTransient);
          add(itemStartOptionsPolicy);
          updateNameOfActionStartBundles();
        }
      };
  }

  void updateNameOfActionStartBundles()
  {
    boolean trans  = itemStartOptionsTransient.getState();
    boolean policy = itemStartOptionsPolicy.getState();

    final String name = Strings.get("item_startbundles")
      +" ("
      +(trans  ? Strings.get("start_option_transient") : "")
      +(trans  ? ", " : "")
      +Strings.get(policy ? "start_option_policy" : "start_option_eager")
      +")";
    actionStartBundles.putValue(Action.NAME, name);
  }


  // Get start option settings from the menu.
  int getStartOptions()
  {
    int options = 0;
    if (itemStartOptionsTransient.getState()) {
      options |= Bundle.START_TRANSIENT;
    }
    if (itemStartOptionsPolicy.getState()) {
      options |= Bundle.START_ACTIVATION_POLICY;
    }
    return options;
  }



  JMenu viewMenu = null;
  JMenu editMenu = null;

  JMenu makeViewMenu(JMenu oldMenu) {
    if (consoleSwing==null) return null; // Desktop already stopped

    JMenu menu;

    if(oldMenu != null) {
      oldMenu.removeAll();
      menu = oldMenu;
    } else {
      menu = new JMenu(Strings.get("menu_view"));
    }

    final ButtonGroup group = new ButtonGroup();

    menu.add(new JCheckBoxMenuItem(Strings.get("menu_view_console")) {
        {
          setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_1,
                                                ActionEvent.ALT_MASK));
          addActionListener(new SplitAction(splitPane,
                                            consoleSwing.getJComponent()));
          setState(true);
        }
      });

    menu.add(new JCheckBoxMenuItem(Strings.get("menu_view_bundles")) {
        {
          setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_2,
                                                ActionEvent.ALT_MASK));
          addActionListener(new SplitAction(splitPaneHoriz,
                                            bundlePanel));
          setState(true);
        }
      });

    menu.add(new JCheckBoxMenuItem(Strings.get("menu_view_info")) {
        {
          setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_3,
                                                ActionEvent.ALT_MASK));
          addActionListener(new SplitAction(splitPaneHoriz,
                                            detailPanel));
          setState(true);
        }
      });

    menu.add(new JCheckBoxMenuItem(Strings.get("menu_view_toolbar")) {
        {
          setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_4,
                                                ActionEvent.ALT_MASK));
          addActionListener(new ActionListener() {
              public void actionPerformed(ActionEvent ev) {
                toolBar.setVisible(getState());
              }
            });
          setState(true);
        }
      });

    menu.add(new JCheckBoxMenuItem(Strings.get("menu_view_statusbar")) {
        {
          setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_5,
                                                ActionEvent.ALT_MASK));
          addActionListener(new ActionListener() {
              public void actionPerformed(ActionEvent ev) {
                statusBar.setVisible(getState());
              }
            });
          setState(false);
        }
      });
    statusBar.setVisible(false);

    menu.add(new JSeparator());

    int count = 0;
    menuMap.clear();
    for(Iterator it = displayMap.keySet().iterator(); it.hasNext(); ) {
      final ServiceReference     sr   = (ServiceReference)it.next();
      final String   name = (String)sr.getProperty(SwingBundleDisplayer.PROP_NAME);
      final int c2 = count++;

      menu.add(new JRadioButtonMenuItem(name) {
          {
            setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_1 + c2,
                                                  mask));
            setMnemonic(KeyEvent.VK_1 + c2);
            menuMap.put(sr, this);
            addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent ev) {
                  bundlePanelShowTab(sr);
                }
              });
            group.add(this);
          }});
    }

    lfMenu = new LookAndFeelMenu(Strings.get("menu_lookandfeel"), lfManager);

    lfMenu.addRoot(SwingUtilities.getRoot(frame));
    menu.add(new JSeparator());
    menu.add(lfMenu);

    menu.add(makeErrorDialogMenu());

    return menu;
  }


  JMenu edlMenu = null;

  JMenu makeErrorDialogMenu() {
    return new JMenu(Strings.get("menu_errordialog")) {
      {
        add(new JCheckBoxMenuItem(Strings.get("menu_errordialog_use")) {
          {
            addActionListener(new ActionListener() {
              public void actionPerformed(ActionEvent ev) {
                System.setProperty("org.knopflerfish.desktop.dontuseerrordialog", String.valueOf(!getState()));
                edlMenu.setEnabled(getState());
              }
            });
            setState(!Util.getBooleanProperty("org.knopflerfish.desktop.dontuseerrordialog", false));
          }
        });
        edlMenu = new JMenu(Strings.get("menu_errordialoglevel")) {
          {
            ButtonGroup group = new ButtonGroup();

            AbstractButton jrbn = new JRadioButtonMenuItem(Strings.get("menu_errordialoglevel_normal"));
            group.add(jrbn);
            add(jrbn);
            jrbn.addActionListener(new ActionListener() {
              public void actionPerformed(ActionEvent ev) {
                System.setProperty("org.knopflerfish.desktop.errordialogfriendliness", "normal");
              }
            });

            AbstractButton jrbm = new JRadioButtonMenuItem(Strings.get("menu_errordialoglevel_more"));
            group.add(jrbm);
            add(jrbm);
            jrbm.addActionListener(new ActionListener() {
              public void actionPerformed(ActionEvent ev) {
                System.setProperty("org.knopflerfish.desktop.errordialogfriendliness", "more");
              }
            });

            AbstractButton jrba = new JRadioButtonMenuItem(Strings.get("menu_errordialoglevel_advanced"));
            group.add(jrba);
            add(jrba);
            jrba.addActionListener(new ActionListener() {
              public void actionPerformed(ActionEvent ev) {
                System.setProperty("org.knopflerfish.desktop.errordialogfriendliness", "advanced");
              }
            });

            String curr = Util.getProperty("org.knopflerfish.desktop.errordialogfriendliness", null);
            if ("more".equals(curr)) {
              group.setSelected(jrbm.getModel(), true);
            } else if ("advanced".equals(curr)) {
              group.setSelected(jrba.getModel(), true);
            } else {
              group.setSelected(jrbn.getModel(), true);
            }

            setEnabled(!Util.getBooleanProperty("org.knopflerfish.desktop.dontuseerrordialog", false));

          }
        };
        add(edlMenu);
      }
    };
  }


  JMenu makeHelpMenu() {
    return new JMenu(Strings.get("menu_help")) {
        {
          add(new JMenuItem(Strings.get("str_about")) {
              {
                addActionListener(new ActionListener() {
                    public void actionPerformed(ActionEvent ev) {
                      showVersion();
                    }
                  });
              }
            });

          add(new JMenuItem(Strings.get("menu_tips")) {
              {
                addActionListener(new ActionListener() {
                    public void actionPerformed(ActionEvent ev) {
                      showTips();
                    }
                  });
              }
            });


          add(new JSeparator());

          add(new JMenuItem(Strings.get("str_fwinfo")) {
              {
                addActionListener(new ActionListener() {
                    public void actionPerformed(ActionEvent ev) {
                      showInfo();
                    }
                  });
              }
            });

          add(new JMenuItem(Strings.get("str_checkupdate")) {
              {
                addActionListener(new ActionListener() {
                    public void actionPerformed(ActionEvent ev) {
                      checkUpdate(true);
                    }
                  });
              }
            });


        }
      };
  }

  Map makeBundleBuckets() {
    Map buckets;

    Bundle[] bl      = Activator.getBundles();
    Map bundles = new HashMap();
    for(int i = 0; bl != null && i < bl.length; i++) {
      Object key = new Long(bl[i].getBundleId());
      bundles.put(key, bl[i]);
    }

    if(bundles.size() > 12) {
      // make alphabetical submenu grouping
      // if number of bundles is large
      buckets = new TreeMap();
      for(Iterator it = bundles.keySet().iterator(); it.hasNext(); ) {
        Object key = it.next();
        Bundle bundle = (Bundle)bundles.get(key);
        String s = Util.getBundleName(bundle);
        String f = s.length() > 0
          ? s.substring(0, 1).toUpperCase()
          : "--";
        Collection bucket = (Collection)buckets.get(f);
        if(bucket == null) {
          bucket = new ArrayList();
          buckets.put(f, bucket);
        }
        bucket.add(bundle);
      }
    } else {
      buckets = new LinkedHashMap();
      for(Iterator it = bundles.keySet().iterator(); it.hasNext(); ) {
        Object key = it.next();
        Bundle bundle = (Bundle)bundles.get(key);

        String f = "#" + bundle.getBundleId() + " " + Util.getBundleName(bundle);
        buckets.put(f, bundle);
      }
    }
    return buckets;
  }


  JMenu makeEditMenu() {
    JMenu menu = new JMenu(Strings.get("menu_edit"));
    updateEditMenu(menu);
    return menu;
  }

  void updateEditMenu(JMenu editMenu) {
    editMenu.add(new JMenuItem(Strings.get("item_unselectall")) {
        {
          addActionListener(new ActionListener() {
              public void actionPerformed(ActionEvent ev) {
                bundleSelModel.clearSelection();
                contentPane.invalidate();
              }
            });
        }
      });
    editMenu.add(new JMenuItem(Strings.get("item_clear_console")) {
        {
          addActionListener(new ActionListener() {
              public void actionPerformed(ActionEvent ev) {
                consoleSwing.clearConsole();
              }
            });
        }
      });

    editMenu.add(new JSeparator());

    JMenu selectMenu = new JMenu("Select bundle");
    editMenu.add(selectMenu);

    Map buckets = makeBundleBuckets();

    for(Iterator it = buckets.keySet().iterator(); it.hasNext(); ) {
      Object key = it.next();
      Object val = buckets.get(key);
      if(val instanceof Collection) {
        Collection bucket = (Collection)val;
        JMenu subMenu = new JMenu(key.toString());
        for(Iterator it2 = bucket.iterator(); it2.hasNext(); ) {
          Bundle bundle = (Bundle)it2.next();
          JMenuItem item = makeSelectBundleItem(bundle);
          subMenu.add(item);
        }
        selectMenu.add(subMenu);
      } else if(val instanceof Bundle) {
        Bundle bundle = (Bundle)val;
        JMenuItem item = makeSelectBundleItem(bundle);
        selectMenu.add(item);
      } else {
        throw new RuntimeException("Unknown object=" + val);
      }
    }
  }


  JMenuItem makeSelectBundleItem(final Bundle bundle) {
    JMenuItem item = new JMenuItem(bundle.getBundleId() + " " + Util.getBundleName(bundle));

    item.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent ev) {
          bundleSelModel.clearSelection();
          bundleSelModel.setSelected(bundle.getBundleId(), true);
        }
      });
    return item;
  }

  /**
   * Helper command class to show/hide splitpane components
   */
  class SplitAction implements ActionListener {
    int        divloc = 0;
    JSplitPane pane;
    JComponent target;

    SplitAction(JSplitPane pane, JComponent target) {
      this.pane   = pane;
      this.target = target;
    }

    public void actionPerformed(ActionEvent ev) {
      boolean b = target.isVisible();

      if(b) {
        divloc = pane.getDividerLocation();
      }

      target.setVisible(!b);

      if(!b) {
        pane.setDividerLocation(divloc);
      }
      pane.getParent().invalidate();
    }
  }


  public void stopFramework() {
    if(SwingUtilities.isEventDispatchThread()) {
      stopFramework0();
    } else {
      SwingUtilities.invokeLater(new Runnable() {
          public void run() {
            stopFramework0();
          }
        });
    }
  }

  protected void stopFramework0() {

    Object[] options = {Strings.get("yes"),
                        Strings.get("cancel")};


    int n =JOptionPane
      .showOptionDialog(frame,
                        Strings.get("q_stopframework"),
                        Strings.get("msg_stopframework"),
                        JOptionPane.YES_NO_OPTION,
                        JOptionPane.QUESTION_MESSAGE,
                        null,
                        options,
                        options[1]);
    if(n == 0) {
      try {
        Bundle sysBundle = Activator.getBC().getBundle((long)0);
        sysBundle.stop();
      } catch (Exception e) {
        showErr("Failed to stop bundle.", e);
      }
    }
  }

  String lastBundleLocation = "http://";

  void addBundleURL() {
    try {
      lastBundleLocation = (String)
        JOptionPane.showInputDialog(frame,
                                    Strings.get("dialog_addbundleurl_msg"),
                                    Strings.get("dialog_addbundleurl_title"),
                                    JOptionPane.QUESTION_MESSAGE,
                                    null,
                                    null,
                                    lastBundleLocation);

      if(lastBundleLocation != null && !"".equals(lastBundleLocation)) {
        Bundle b = Activator.getTargetBC().installBundle(lastBundleLocation);
        Dictionary headers = b.getHeaders();
        if(Util.doAutostart() && Util.canBeStarted(b)) {
          startBundle(b);
        }
      }
    } catch (Exception e) {
      showErr(null, e);
    }
  }

  JFileChooser openFC = null;

  static final String FILE_PROTO = "file:";

  /**
   * Open a file dialog and ask for jar files to install as bundles.
   */
  void addBundle() {
    if(openFC == null) {
      openFC = new JFileChooser();
      File cwd = new File(Util.getProperty("user.dir", "."));
      final String jarsProp=Util.getProperty("org.knopflerfish.gosg.jars",null);
      if (jarsProp!=null) {
        final StringTokenizer st = new StringTokenizer(jarsProp,";");
        while (st.hasMoreTokens()) {
          final String url = st.nextToken().trim();
          if(url.startsWith(FILE_PROTO)) {
            final File dir = new File(url.substring(FILE_PROTO.length()));
            if (dir.exists()) {
              cwd = dir;
              break;
            }
          }
        }
      }
      // The argument to setCurrentDirectory() must be an ablsolute
      // path on MacOSX!
      openFC.setCurrentDirectory(cwd.getAbsoluteFile());
      openFC.setMultiSelectionEnabled(true);
      FileFilterImpl filter = new FileFilterImpl();
      filter.addExtension("jar");
      filter.setDescription("Jar files");
      openFC.setFileFilter(filter);
      openFC.setDialogTitle("Open bundle jar file");
      openFC.setApproveButtonText("Open");
    }

    int returnVal = openFC.showOpenDialog(frame);

    if(returnVal == JFileChooser.APPROVE_OPTION) {
      File[] files = openFC.getSelectedFiles();

      for(int i = 0; i < files.length; i++) {
        addFile(files[i]);
      }
    }
  }

  void doConnect() {
    String[] options = new String[Activator.remoteHosts.size()];
    Activator.remoteHosts.copyInto(options);

    // The selection comp I want in the dialog
    JComboBox combo = new JComboBox(options);
    combo.setEditable(true);


    // Mindboggling complicate way of creating an option dialog
    // without the auto-generated input field

    JLabel msg    = new JLabel(Strings.get("remote_connect_msg"));
    JPanel panel  = new JPanel(new BorderLayout());

    panel.add(combo, BorderLayout.SOUTH);
    panel.add(msg,   BorderLayout.NORTH);

    JOptionPane optionPane = new JOptionPane(panel,
                                             JOptionPane.QUESTION_MESSAGE);
    optionPane.setIcon(connectIconLarge);
    optionPane.setRootFrame(frame);
    optionPane.setOptionType(JOptionPane.OK_CANCEL_OPTION);
    optionPane.setWantsInput(false);
    optionPane.setOptions(new String[]
      {
        Strings.get("ok"),
        Strings.get("cancel"),
        Strings.get("local"),
      });

    optionPane.selectInitialValue();

    JDialog dialog =
      optionPane.createDialog(frame,
                              Strings.get("remote_connect_title"));
    dialog.setVisible(true);
    dialog.dispose();

    if (!(optionPane.getValue() instanceof String)) { // We'll get an Integer if the user pressed Esc
      return;
    }

    String value = (String)optionPane.getValue();

    if (Strings.get("cancel").equals(value)) {
      return;
    }

    String s = (String)combo.getSelectedItem();

    if (Strings.get("local").equals(value)) {
      s = "";
    }

    if(!Activator.remoteHosts.contains(s)) {
      Activator.remoteHosts.addElement(s);
    }

    if ((s != null)) {
      Activator.openRemote(s);
    }
  }

  JFileChooser saveFC = null;

  void save() {
    if(saveFC == null) {
      saveFC = new JFileChooser();
      saveFC.setCurrentDirectory(new File("."));
      saveFC.setMultiSelectionEnabled(false);
      FileFilterImpl filter = new FileFilterImpl();
      filter.addExtension("jar");
      filter.addExtension("zip");
      filter.setDescription("Deploy archives");
      saveFC.setFileFilter(filter);
      saveFC.setDialogTitle("Save deploy archive");
      saveFC.setApproveButtonText("Save");
    }

    Bundle[] targets    = getSelectedBundles();

    StringBuffer title = new StringBuffer();
    title.append("Save deploy archive of: ");

    for(int i = 0; i < targets.length; i++) {
      title.append(Util.getBundleName(targets[i]));
      if(i < targets.length - 1) {
        title.append(", ");
      }
    }
    saveFC.setDialogTitle(title.toString());

    int returnVal = saveFC.showSaveDialog(frame);

    if(returnVal == JFileChooser.APPROVE_OPTION) {
      File file = saveFC.getSelectedFile();

      doSave(file, targets);
    }
  }

  void doSave(File file, Bundle[] targets) {
    byte[] buf = new byte[1024 * 5];

    if(file.getName().endsWith(".jar") ||
       file.getName().endsWith(".zip")) {
      // OK
    } else {
      file = new File(file.getAbsolutePath() + ".jar");
    }

    if(file.exists()) {
      Object[] options = { Strings.get("yes"),
                           Strings.get("cancel")};

      int n = JOptionPane.showOptionDialog(frame,
                                           file.getAbsolutePath() + "\n" +
                                           "already exist.\n\n" +
                                           "Overwrite file?",
                                           "File exists",
                                           JOptionPane.YES_NO_OPTION,
                                           JOptionPane.QUESTION_MESSAGE,
                                           null,
                                           options,
                                           options[1]);
      if(n == 1) {
        return;
      }
    }


    String base = file.getName();
    int ix = base.lastIndexOf(".");
    if(ix != -1) {
      base = base.substring(0, ix);
    }

    final PackageAdmin pkgAdmin = (PackageAdmin) pkgTracker.getService();
    if(pkgAdmin == null) {
      Activator.log.error("No pkg admin available for save");
      return;
    }

    Bundle[] allBundles = Activator.getBundles();


    Set pkgClosure = new TreeSet(Util.bundleIdComparator);

    for(int i = 0; i < targets.length; i++) {
      pkgClosure.addAll(Util.getPackageClosure(pm,
                                               targets[i],
                                               null));
    }

    Set serviceClosure = new TreeSet(Util.bundleIdComparator);

    for(int i = 0; i < targets.length; i++) {
      serviceClosure.addAll(Util.getServiceClosure(targets[i], null));
    }

    Set all = new TreeSet(Util.bundleIdComparator);
    all.addAll(pkgClosure);
    all.addAll(serviceClosure);

    for(int i = 0; i < targets.length; i++) {
      all.add(targets[i]);
    }

    // remove system bundle.
    all.remove(Activator.getTargetBC().getBundle(0));

    ZipOutputStream out = null;

    StartLevel sl = (StartLevel)slTracker.getService();

    File jarunpackerFile = new File("../tools/jarunpacker/out/jarunpacker/jarunpacker.jar");

    URL jarunpackerURL = null;

    try {
      jarunpackerURL = getClass().getResource("/jarunpacker.jar");
    } catch (Exception ignored) {
    }

    InputStream jarunpacker_in = null;

    try {
      if(file.getName().endsWith(".jar")) {


        if(jarunpackerURL != null) {
          jarunpacker_in = jarunpackerURL.openStream();
        } else if(jarunpackerFile.exists()) {
          jarunpacker_in = new FileInputStream(jarunpackerFile);
        }

        if(jarunpacker_in != null) {
          // Construct a string version of a manifest
          StringBuffer sb = new StringBuffer();
          sb.append("Manifest-Version: 1.0\n");
          sb.append("Main-class: org.knopflerfish.tools.jarunpacker.Main\n");
          sb.append("jarunpacker-optbutton: base\n");
          sb.append("jarunpacker-destdir: .\n");
          sb.append("knopflerfish-version: " + base + "\n");
          sb.append("jarunpacker-opendir: " + base + "\n");

          // Convert the string to a input stream
          InputStream is = new ByteArrayInputStream(sb.toString().getBytes("UTF-8"));
          Manifest mf = new Manifest(is);

          out = new JarOutputStream(new FileOutputStream(file), mf);
        } else {
          out = new JarOutputStream(new FileOutputStream(file));
        }
      } else if(file.getName().endsWith(".zip")) {
        out = new ZipOutputStream(new FileOutputStream(file));
      }

      StringBuffer xargs = new StringBuffer();

      int levelMax = -1;

      int bid = 0;
      int lastLevel = -1;
      for(Iterator it = all.iterator(); it.hasNext(); ) {
        Bundle b   = (Bundle)it.next();
        String loc = b.getLocation();

        bid++;

        URL srcURL = new URL(loc);

        String name = Util.shortLocation(loc);

        ZipEntry entry = new ZipEntry(base + "/" + name);

        int level     = -1;

        try {
          level = sl.getBundleStartLevel(b);
        } catch (Exception ignored) {        }

        levelMax = Math.max(level, levelMax);

        if(level != -1 && level != lastLevel) {
          xargs.append("-initlevel " + level + "\n");
          lastLevel = level;
        }

        xargs.append("-install file:" + name + "\n");

        out.putNextEntry(entry);

        InputStream in = null;
        try {
          in = srcURL.openStream();
          int n = 0;
          while ((n = in.read(buf)) != -1) {
            out.write(buf, 0, n);
          }
        } finally {
          try {
            in.close();
          } catch (Exception ignored) { }
        }
      }

      bid = 0;
      for(Iterator it = all.iterator(); it.hasNext(); ) {
        Bundle b   = (Bundle)it.next();
        bid++;

        if(b.getState() == Bundle.ACTIVE) {
          xargs.append("-start " + bid + "\n");
        }
      }

      if(levelMax != -1) {
        xargs.append("-startlevel " + levelMax + "\n");
      }

      ZipEntry entry = new ZipEntry(base + "/" + "init.xargs");
      out.putNextEntry(entry);
      out.write(xargs.toString().getBytes());

      entry = new ZipEntry(base + "/" + "framework.jar");
      out.putNextEntry(entry);

      InputStream in = null;

      File fwFile = new File("framework.jar");
      if(fwFile.exists()) {
        try {
          in = new FileInputStream(fwFile);
          int n = 0;
          while ((n = in.read(buf)) != -1) {
            out.write(buf, 0, n);
          }
        } finally {
          try {
            in.close();
          } catch (Exception ignored) { }
        }
      } else {
        Activator.log.warn("No framework.jar file found");
      }


      // Copy jarunpacker files, if availbale
      if(jarunpacker_in != null) {
        JarInputStream jar_in = null;

        try {
          jar_in = new JarInputStream(new BufferedInputStream(jarunpacker_in));

          ZipEntry srcEntry;
          while(null != (srcEntry = jar_in.getNextEntry())) {

            // Skip unused files from jarunpacker
            if(srcEntry.getName().startsWith("META-INF") ||
               srcEntry.getName().startsWith("OSGI-OPT")) {
              continue;
            }

            ZipEntry destEntry = new ZipEntry(srcEntry.getName());

            out.putNextEntry(destEntry);

            long nTotal = 0;
            int n = 0;
            while (-1 != (n = jar_in.read(buf, 0, buf.length))) {
              out.write(buf, 0, n);
              nTotal += n;
            }
          }
        } finally {
          try { jar_in.close()} catch (Exception ignored) {  }
        }
      } else {
        Activator.log.warn("No jarunpacker available");
      }
      // end of jarunpacker copy

    } catch (Exception e) {
      showErr("Failed to write to " + file, e);
      Activator.log.error("Failed to write to " + file, e);
    } finally {
      try { out.close(); } catch (Exception ignored) { }
    }


    String txt =
      "Saved deploy archive as\n\n" +
      "  " + file.getAbsolutePath() + "\n\n" +
      "To run, unpack the archive and run with\n\n" +
      "  java -jar framwork.jar\n";

    JOptionPane.showMessageDialog(frame,
                                  txt,
                                  "Saved deploy archive",
                                  JOptionPane.INFORMATION_MESSAGE,
                                  null);
  }



  public Bundle[] getSelectedBundles() {
    final ArrayList res = new ArrayList(bundleCache.length);

    for(int i = 0; i < bundleCache.length; i++) {
      final Bundle b = bundleCache[i];
      if(isSelected(b)) {
        res.add(b);
      }
    }
    return (Bundle[]) res.toArray(new Bundle[res.size()]);
  }

  void startBundle(final Bundle b) {
    // Must not call start() from the EDT, since that will block the
    // EDT untill the start()-call completes.
    new Thread("Desktop-StartBundle "+b.getBundleId())
    {
      public void run()
      {
        try {
          b.start(getStartOptions());
        } catch (Exception e) {
          showErr("Failed to start bundle " +Util.getBundleName(b) +": " +e, e);
        }
      }
    }.start();
  }

  void stopBundles(Bundle[] bl) {
    for(int i = 0; bl != null && i < bl.length; i++) {
      Bundle b = bl[i];
      stopBundle(b);
    }
  }
  void startBundles(Bundle[] bl) {
    for(int i = 0; bl != null && i < bl.length; i++) {
      Bundle b = bl[i];
      startBundle(b);
    }
  }

  void updateBundles(Bundle[] bl) {
    for(int i = 0; bl != null && i < bl.length; i++) {
      Bundle b = bl[i];
      updateBundle(b);
    }
  }

  void uninstallBundles(Bundle[] bl) {
    for(int i = 0; bl != null && i < bl.length; i++) {
      Bundle b = bl[i];
      uninstallBundle(b, true);
    }
  }



  void stopBundle(final Bundle b) {
    // Special handling needed when stopping the desktop itself.
    final boolean stoppingSelf = b.getBundleId() == 0
      || b.getBundleId() == Activator.getTargetBC().getBundle().getBundleId();

    int n = 0;
    if(stoppingSelf) {
      final Object[] options = { Strings.get("yes"),
                                 Strings.get("no")};
      n = JOptionPane
        .showOptionDialog(frame,
                          Strings.fmt("fmt_q_stopdesktop",
                                      Util.getBundleName(b)),
                          Strings.get("yes"),
                          JOptionPane.YES_NO_OPTION,
                          JOptionPane.QUESTION_MESSAGE,
                          null,
                          options,
                          options[1]);
    }

    if(n == 0) {
      // Must not call stop() from the EDT, since that will block the
      // EDT untill the stop()-call completes.
      new Thread("Desktop-StopBundle " +b.getBundleId())
      {
        public void run()
        {
          try {
            b.stop(getStopOptions());
          } catch (Exception e) {
            showErr("Failed to stop bundle " +Util.getBundleName(b) +": " +e,
                    e);
          }
        }
      }.start();
    }
  }

  void refreshBundle(final Bundle[] b) {
    final ServiceReference sr
      = Activator.getTargetBC_getServiceReference(PackageAdmin.class.getName());

    if(sr != null) {
      final PackageAdmin packageAdmin = (PackageAdmin)
        Activator.getTargetBC_getService(sr);

      if(packageAdmin != null) {
        final boolean refreshAll = b==null || 0==b.length;
        // Must not call refreshPackages() from the EDT, since that
        // will may the EDT.

        final StringBuffer sb = new StringBuffer("Desktop-RefreshPackages ");
        if (refreshAll) {
          sb.append("all packages pending removal");
        } else {
          sb.append("bundle packages for ");
          for (int i=0; i<b.length; i++) {
            if (i>0) {
              sb.append(", ");
            }
            sb.append(b[i].getBundleId());
          }
        }

        new Thread(sb.toString())
        {
          public void run()
          {
            try {
              packageAdmin.refreshPackages( refreshAll ? null : b);
            } catch (Exception e) {
              showErr(sb.toString() + " failed to refresh bundles: "+e , e);
            } finally {
              Activator.getTargetBC().ungetService(sr);
            }
          }
        }.start();

      }
    }
  }

  void resolveBundles(final Bundle[] b) {
    final ServiceReference sr = Activator.getTargetBC()
      .getServiceReference(PackageAdmin.class.getName());

    if(sr != null) {
      final PackageAdmin packageAdmin = (PackageAdmin)
        Activator.getTargetBC().getService(sr);

      if(packageAdmin != null) {
        final boolean resolveAll = b==null || 0==b.length;
        // Must not call resolve() from the EDT, since that will block
        // the EDT.

        final StringBuffer sb = new StringBuffer("Desktop-ResolveBundles: ");
        if (resolveAll) {
          sb.append("all bundles needing to be resolved ");
        } else {
          sb.append("selected bundle(s) ");
          for (int i=0; i<b.length; i++) {
            if (i>0) {
              sb.append(", ");
            }
            sb.append(b[i].getBundleId());
          }
        }

        new Thread(sb.toString())
        {
          public void run()
          {
            try {
              if (!packageAdmin.resolveBundles( resolveAll ? null : b)) {
                showErr(sb.toString() + "; could not resolve all of them.",
                        null);
              }
            } catch (Exception e) {
              showErr(sb.toString() + " failed to resolve bundles: "+e , e);
            } finally {
              Activator.getTargetBC().ungetService(sr);
            }
          }
        }.start();

      }
    }
  }

  void updateBundle(final Bundle b) {
    new Thread("Desktop-UpdateBundle " +b.getBundleId())
    {
      public void run()
      {
        try {
          b.update();
        } catch (Exception e) {
          showErr("Failed to update bundle " +Util.getBundleName(b) +": " +e ,
                  e);
        }
      }
    }.start();
  }

  boolean uninstallBundle(final Bundle b, boolean bAsk) {
    Object[] options = {Strings.get("yes"),
                        Strings.get("no")};
    int n = bAsk
      ? JOptionPane
      .showOptionDialog(frame,
                        Strings.fmt("q_uninstallbundle",
                                     Util.getBundleName(b)),
                        Strings.get("msg_uninstallbundle"),
                        JOptionPane.YES_NO_OPTION,
                        JOptionPane.QUESTION_MESSAGE,
                        null,
                        options,
                        options[1])
      : 0;

    if(n == 0) {
      // Must not call uninstall() from the EDT, since that will block
      // the EDT untill the uninstall()-call completes.
      new Thread("Desktop-UninstallBundle " +b.getBundleId())
      {
        public void run()
        {
          try {
            b.uninstall();
          } catch (Exception e) {
            showErr("failed to uninstall bundle " + Util.getBundleName(b), e);
          }
        }
      }.start();
    }
    return false;
  }




  void showErr(String msg, Exception e) {
    Throwable t = e;
    if (null!=t) {
      while(t instanceof BundleException &&
            ((BundleException) t).getNestedException() != null) {
        t = ((BundleException) t).getNestedException();
      }
    }
    if (Util.getBooleanProperty("org.knopflerfish.desktop.dontuseerrordialog",
                                false)) {
      if(msg != null && !"".equals(msg)) {
        System.out.println(msg);
      }
      if (null!=t) {
        t.printStackTrace();
      }
    } else {
      new ErrorMessageDialog(frame, null, msg, null, t).setVisible(true);
    }
  }



  // DropTargetListener
  public void drop(DropTargetDropEvent e) {

    // This code is f***ing unbelievable.
    // How is anyone supposed to create it from scratch?
    try {
      DataFlavor[] dfl = e.getCurrentDataFlavors();
      Transferable tr  = e.getTransferable();

      if(e.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
        e.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
        java.util.List files = (java.util.List)tr.getTransferData(DataFlavor.javaFileListFlavor);
        for(Iterator it = files.iterator(); it.hasNext();) {
          File file = (File)it.next();
          addFile(file);
        }
        e.dropComplete(true);
      } else if(e.isDataFlavorSupported(DataFlavor.stringFlavor)) {
        e.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
        String filename = (String)tr.getTransferData(DataFlavor.stringFlavor);

        addFile(new File(filename));
        e.dropComplete(true);
      } else {
        // Reject drop
      }
    }
    catch(IOException ioe) {
      showErr(null, ioe);
    }
    catch(UnsupportedFlavorException ufe) {
      showErr("Unsupported data type", ufe);
    }
  }

  // DropTargetListener
  public void dragEnter(DropTargetDragEvent e) {
    //    System.out.println("dragEnter " + e);
  }

  // DropTargetListener
  public void dragExit(DropTargetEvent e) {
    //    System.out.println("dragExit " + e);
  }

  // DropTargetListener
  public void dragOver(DropTargetDragEvent e) {
    //    System.out.println("dragOver " + e);
  }

  // DropTargetListener
  public void dropActionChanged(DropTargetDragEvent e) {
    //    System.out.println("dropActionChanged " + e);
  }


  void addFile(File file) {
    try {
      String location = "file:" + file.getAbsolutePath();
      Bundle b = Activator.getTargetBC().installBundle(location);

      if(Util.doAutostart() && Util.canBeStarted(b)) {
        startBundle(b);
      }
    } catch (Exception e) {
      if(file.getName().toUpperCase().endsWith(".JAR")) {
        showErr("Failed to open bundle.", e);
      } else {
        showErr("The file is not a bundle.", e);
      }
    }
  }

  // try to show a bundle in displayers
  public void showBundle(Bundle b) {
    Object[] disps = dispTracker.getServices();
    for(int i = 0; disps != null && i < disps.length; i++) {
      SwingBundleDisplayer disp = (SwingBundleDisplayer)disps[i];
      disp.showBundle(b);
    }
  }

  public void stop() {
    for(Iterator it = sizesavers.iterator(); it.hasNext(); ) {
      SizeSaver ss = (SizeSaver)it.next();
      ss.detach();
    }
    sizesavers.clear();

    if(tips != null) {
      tips.setVisible(false);
      tips = null;
    }

    alive = false;

    slTracker.close();
    dispTracker.close();
    pkgTracker.close();

    // Make sure floating windows are closed
    for(int i = 0; i < detailPanel.getTabCount(); i++) {
      Component comp = detailPanel.getComponentAt(i);
      if(comp instanceof JFloatable) {
        ((JFloatable)comp).setAutoClose(true);
      }
    }

    Activator.getTargetBC().removeBundleListener(this);

    if(consoleSwing != null) {
      consoleSwing.stop();
      consoleSwing = null;
    }


    if(frame != null) {
      frame.setVisible(false);
      frame = null;
    }

    // If running on Mac OS, remove eawt Application handlers.
    if(bMacOS) {
      try {
        OSXAdapter.clearApplicationListeners();
      } catch (Exception e) {
        Activator.log.warn("Error while using the OSXAdapter", e);
        bMacOS = false;
      }
    }

  }

  public void valueChanged(long bid) {
    if(!alive) {
      return;
    }

    updateBundleViewSelections();
  }

  public void frameworkEvent(FrameworkEvent ev) {
    if(!alive) {
      return;
    }
    switch(ev.getType()) {
    case FrameworkEvent.STARTLEVEL_CHANGED:
      updateStartLevel();
      break;
    }
  }

  volatile Bundle[] bundleCache;


  public void bundleChanged(final BundleEvent ev) {
    if(!alive) {
      return;
    }

    // This callback method is marked as "NotThreadSafe" in the
    // javadoc, that implies that the thread calling it may hold
    // internal framework locks, thus since we need to call into the
    // framework all work must be done on a separate thread to avoid
    // dead-locks.
    final Bundle bundle = null!=ev ? ev.getBundle() : null;
    final String threadName = "Desktop.bundleChanged("
      +(null==ev ? "" :
        (Util.bundleEventName(ev.getType()) +", "
         +(null==bundle ? "*" : String.valueOf(bundle.getBundleId()))))
      +")";

    final Thread bct = new Thread(threadName) {
        public void run()
        {
          if (null!=ev) {
            if (BundleEvent.UPDATED==ev.getType()) {
              // An updated bundle may have changed icon...
              Util.clearBundleIcon(ev.getBundle());
            }
          }

          if(pm != null) {
            pm.refresh();
          }
          bundleCache = Activator.getBundles();

          SwingUtilities.invokeLater(new Runnable() {
              public void run() {
                if (ev!=null && BundleEvent.INSTALLED==ev.getType()) {
                  // Select bundle when installed
                  setSelected(bundle);
                  showBundle(bundle);
                } else if (null!=bundle && isSelected(bundle)) {
                  if (BundleEvent.UNINSTALLED==ev.getType()) {
                    bundleSelModel.setSelected(bundle.getBundleId(), false);
                  } else {
                    // Trigger a selection change notification to tell
                    // displayers to update their contents
                    if (bundleSelModel instanceof DefaultBundleSelectionModel) {
                      final DefaultBundleSelectionModel dbsm
                        = (DefaultBundleSelectionModel) bundleSelModel;
                      dbsm.fireChange(bundle.getBundleId());
                    }
                  }
                }
                updateStatusBar();
                updateMenus();
                toolBar.revalidate();
                toolBar.repaint();
              }
            });
        }
      };
    bct.start();
  }

  void updateStatusBar() {
  }


  JTips tips = null;
  void showTips() {
    if(tips == null) {
      tips = new JTips("/tips.html");
    }
    tips.setVisible(true);
  }

  static final String KEY_UPDATEVERSION = "updateVersion";

void showUpdate(Version sysVersion, Version version, String notes) {
   Preferences prefs = Preferences.userNodeForPackage(getClass());

   Activator.log.info("showUpdate sysVersion=" + sysVersion + ", version=" + version);
   try {
     String prefsVersionS = prefs.get(KEY_UPDATEVERSION, "");
     if(prefsVersionS != null && !"".equals(prefsVersionS)) {
       Version prefsVersion = new Version(prefsVersionS);
       Activator.log.info("prefsVersion=" + prefsVersion);
       if(prefsVersion.compareTo(version) >= 0) {
         Activator.log.info("skip showUpdate " + version);
         return;
       }
     }
   } catch (Exception e) {
     Activator.log.warn("Failed to compare prefs version", e);
   }

   JTextPane html = new JTextPane();

    html.setContentType("text/html");

    html.setEditable(false);

    html.setText("<pre>\n" +
                 notes +
                 "\n</pre>");

    final JScrollPane scroll = new JScrollPane(html);
    scroll.setPreferredSize(new Dimension(500, 300));
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
          JViewport vp = scroll.getViewport();
          if(vp != null) {
            vp.setViewPosition(new Point(0,0));
            scroll.setViewport(vp);
          }
        }
      });

    JOptionPane.showMessageDialog(frame,
                                  scroll,
                                  "Update available",
                                  JOptionPane.INFORMATION_MESSAGE,
                                  null);

    try {
      prefs.put(KEY_UPDATEVERSION, version.toString());
      Activator.log.info("saved version " + version);
      prefs.flush();
    } catch (Exception e) {
      Activator.log.warn("Failed to store prefs", e);
    }
}


  void showInfo() {
    JTextPane html = new JTextPane();

    html.setContentType("text/html");

    html.setEditable(false);

    html.setText(Util.getSystemInfo());

    final JScrollPane scroll = new JScrollPane(html);
    scroll.setPreferredSize(new Dimension(420, 300));
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
          JViewport vp = scroll.getViewport();
          if(vp != null) {
            vp.setViewPosition(new Point(0,0));
            scroll.setViewport(vp);
          }
        }
      });

    JOptionPane.showMessageDialog(frame,
                                  scroll,
                                  "Framework info",
                                  JOptionPane.INFORMATION_MESSAGE,
                                  null);
  }

  public void showVersion() {
    BundleContext bc = Activator.getBC();
    String version = (String)bc.getBundle().getHeaders().get("Bundle-Version");
    String txt = Strings.fmt("str_abouttext",
                             version,
                             bc.getProperty(org.osgi.framework.Constants.FRAMEWORK_VENDOR),
                             bc.getBundle(0).getHeaders().get("Bundle-Version"));

    ImageIcon icon =
      new ImageIcon(getClass().getResource("/kf_300x170.png"));

    JOptionPane.showMessageDialog(frame,
                                  txt,
                                  Strings.get("str_about"),
                                  JOptionPane.INFORMATION_MESSAGE,
                                  icon);
  }

  public void setIcon(JFrame frame, String baseName) {
    // Frame icon
    final String iconName1
      = baseName +(Util.isWindows() ? "16x16.png" : "32x32.png");
    // Max OS X dock icon
    final String iconName2 = baseName +"128x128.png";
    final MediaTracker tracker = new MediaTracker(frame);
    try {
      final URL[] urls = new URL[]{
        getClass().getResource(iconName1),
        getClass().getResource(iconName2)
      };

      final Image[] images = new Image[urls.length];
      for (int i=0; i<urls.length; i++) {
        if(urls[i] != null) {
          images[i] = frame.getToolkit().getImage(urls[i]);
          tracker.addImage(images[i], 0);
        }
      }
      tracker.waitForID(0);

      if (null!=images[0]) frame.setIconImage(images[0]);
      // Set the dock icon.
      if(null!=images[1] && bMacOS) {
        try {
          OSXAdapter.setDockIconImage(images[1]);
        } catch (Exception e) {
          Activator.log.warn("Error while loading the OSXAdapter", e);
        }
      }
    } catch (Exception e) {
    }
  }

  public Icon getBundleEventIcon(int type) {
    switch(type) {
    case BundleEvent.INSTALLED:   return installIcon;
    case BundleEvent.STARTED:     return startIcon;
    case BundleEvent.STOPPED:     return stopIcon;
    case BundleEvent.UNINSTALLED: return uninstallIcon;
    case BundleEvent.UPDATED:     return updateIcon;
    default:                      return null;
    }
  }
}
TOP

Related Classes of org.knopflerfish.bundle.desktop.swing.Desktop$SplitAction

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.