OSDN Git Service

merge from open-source master
[android-x86/sdk.git] / ddms / app / src / com / android / ddms / UIThread.java
1 /*
2  * Copyright (C) 2007 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.ddms;
18
19 import com.android.ddmlib.AndroidDebugBridge;
20 import com.android.ddmlib.Client;
21 import com.android.ddmlib.ClientData;
22 import com.android.ddmlib.IDevice;
23 import com.android.ddmlib.Log;
24 import com.android.ddmlib.SyncException;
25 import com.android.ddmlib.SyncService;
26 import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
27 import com.android.ddmlib.ClientData.IHprofDumpHandler;
28 import com.android.ddmlib.ClientData.MethodProfilingStatus;
29 import com.android.ddmlib.Log.ILogOutput;
30 import com.android.ddmlib.Log.LogLevel;
31 import com.android.ddmuilib.AllocationPanel;
32 import com.android.ddmuilib.DevicePanel;
33 import com.android.ddmuilib.EmulatorControlPanel;
34 import com.android.ddmuilib.HeapPanel;
35 import com.android.ddmuilib.ITableFocusListener;
36 import com.android.ddmuilib.ImageLoader;
37 import com.android.ddmuilib.InfoPanel;
38 import com.android.ddmuilib.NativeHeapPanel;
39 import com.android.ddmuilib.ScreenShotDialog;
40 import com.android.ddmuilib.SysinfoPanel;
41 import com.android.ddmuilib.TablePanel;
42 import com.android.ddmuilib.ThreadPanel;
43 import com.android.ddmuilib.DevicePanel.IUiSelectionListener;
44 import com.android.ddmuilib.actions.ToolItemAction;
45 import com.android.ddmuilib.explorer.DeviceExplorer;
46 import com.android.ddmuilib.handler.BaseFileHandler;
47 import com.android.ddmuilib.handler.MethodProfilingHandler;
48 import com.android.ddmuilib.log.event.EventLogPanel;
49 import com.android.ddmuilib.logcat.LogColors;
50 import com.android.ddmuilib.logcat.LogFilter;
51 import com.android.ddmuilib.logcat.LogPanel;
52 import com.android.ddmuilib.logcat.LogPanel.ILogFilterStorageManager;
53
54 import org.eclipse.jface.dialogs.MessageDialog;
55 import org.eclipse.jface.preference.IPreferenceStore;
56 import org.eclipse.jface.preference.PreferenceStore;
57 import org.eclipse.swt.SWT;
58 import org.eclipse.swt.SWTError;
59 import org.eclipse.swt.SWTException;
60 import org.eclipse.swt.dnd.Clipboard;
61 import org.eclipse.swt.events.ControlEvent;
62 import org.eclipse.swt.events.ControlListener;
63 import org.eclipse.swt.events.MenuAdapter;
64 import org.eclipse.swt.events.MenuEvent;
65 import org.eclipse.swt.events.SelectionAdapter;
66 import org.eclipse.swt.events.SelectionEvent;
67 import org.eclipse.swt.events.ShellEvent;
68 import org.eclipse.swt.events.ShellListener;
69 import org.eclipse.swt.graphics.Color;
70 import org.eclipse.swt.graphics.Font;
71 import org.eclipse.swt.graphics.FontData;
72 import org.eclipse.swt.graphics.Image;
73 import org.eclipse.swt.graphics.Rectangle;
74 import org.eclipse.swt.layout.FillLayout;
75 import org.eclipse.swt.layout.FormAttachment;
76 import org.eclipse.swt.layout.FormData;
77 import org.eclipse.swt.layout.FormLayout;
78 import org.eclipse.swt.layout.GridData;
79 import org.eclipse.swt.layout.GridLayout;
80 import org.eclipse.swt.widgets.Composite;
81 import org.eclipse.swt.widgets.Display;
82 import org.eclipse.swt.widgets.Event;
83 import org.eclipse.swt.widgets.Label;
84 import org.eclipse.swt.widgets.Listener;
85 import org.eclipse.swt.widgets.Menu;
86 import org.eclipse.swt.widgets.MenuItem;
87 import org.eclipse.swt.widgets.MessageBox;
88 import org.eclipse.swt.widgets.Sash;
89 import org.eclipse.swt.widgets.Shell;
90 import org.eclipse.swt.widgets.TabFolder;
91 import org.eclipse.swt.widgets.TabItem;
92 import org.eclipse.swt.widgets.ToolBar;
93 import org.eclipse.swt.widgets.ToolItem;
94
95 import java.io.File;
96 import java.util.ArrayList;
97
98 /**
99  * This acts as the UI builder. This cannot be its own thread since this prevent using AWT in an
100  * SWT application. So this class mainly builds the ui, and manages communication between the panels
101  * when {@link IDevice} / {@link Client} selection changes.
102  */
103 public class UIThread implements IUiSelectionListener, IClientChangeListener {
104     /*
105      * UI tab panel definitions. The constants here must match up with the array
106      * indices in mPanels. PANEL_CLIENT_LIST is a "virtual" panel representing
107      * the client list.
108      */
109     public static final int PANEL_CLIENT_LIST = -1;
110
111     public static final int PANEL_INFO = 0;
112
113     public static final int PANEL_THREAD = 1;
114
115     public static final int PANEL_HEAP = 2;
116
117     public static final int PANEL_NATIVE_HEAP = 3;
118
119     private static final int PANEL_ALLOCATIONS = 4;
120
121     private static final int PANEL_SYSINFO = 5;
122
123     private static final int PANEL_COUNT = 6;
124
125     /** Content is setup in the constructor */
126     private static TablePanel[] mPanels = new TablePanel[PANEL_COUNT];
127
128     private static final String[] mPanelNames = new String[] {
129             "Info", "Threads", "VM Heap", "Native Heap", "Allocation Tracker", "Sysinfo"
130     };
131
132     private static final String[] mPanelTips = new String[] {
133             "Client information", "Thread status", "VM heap status",
134             "Native heap status", "Allocation Tracker", "Sysinfo graphs"
135     };
136
137     private static final String PREFERENCE_LOGSASH =
138         "logSashLocation"; //$NON-NLS-1$
139     private static final String PREFERENCE_SASH =
140         "sashLocation"; //$NON-NLS-1$
141
142     private static final String PREFS_COL_TIME =
143         "logcat.time"; //$NON-NLS-1$
144     private static final String PREFS_COL_LEVEL =
145         "logcat.level"; //$NON-NLS-1$
146     private static final String PREFS_COL_PID =
147         "logcat.pid"; //$NON-NLS-1$
148     private static final String PREFS_COL_TAG =
149         "logcat.tag"; //$NON-NLS-1$
150     private static final String PREFS_COL_MESSAGE =
151         "logcat.message"; //$NON-NLS-1$
152
153     private static final String PREFS_FILTERS = "logcat.filter"; //$NON-NLS-1$
154
155     // singleton instance
156     private static UIThread mInstance = new UIThread();
157
158     // our display
159     private Display mDisplay;
160
161     // the table we show in the left-hand pane
162     private DevicePanel mDevicePanel;
163
164     private IDevice mCurrentDevice = null;
165     private Client mCurrentClient = null;
166
167     // status line at the bottom of the app window
168     private Label mStatusLine;
169
170     // some toolbar items we need to update
171     private ToolItem mTBShowThreadUpdates;
172     private ToolItem mTBShowHeapUpdates;
173     private ToolItem mTBHalt;
174     private ToolItem mTBCauseGc;
175     private ToolItem mTBDumpHprof;
176     private ToolItem mTBProfiling;
177
178     private final class FilterStorage implements ILogFilterStorageManager {
179
180         public LogFilter[] getFilterFromStore() {
181             String filterPrefs = PrefsDialog.getStore().getString(
182                     PREFS_FILTERS);
183
184             // split in a string per filter
185             String[] filters = filterPrefs.split("\\|"); //$NON-NLS-1$
186
187             ArrayList<LogFilter> list =
188                 new ArrayList<LogFilter>(filters.length);
189
190             for (String f : filters) {
191                 if (f.length() > 0) {
192                     LogFilter logFilter = new LogFilter();
193                     if (logFilter.loadFromString(f)) {
194                         list.add(logFilter);
195                     }
196                 }
197             }
198
199             return list.toArray(new LogFilter[list.size()]);
200         }
201
202         public void saveFilters(LogFilter[] filters) {
203             StringBuilder sb = new StringBuilder();
204             for (LogFilter f : filters) {
205                 String filterString = f.toString();
206                 sb.append(filterString);
207                 sb.append('|');
208             }
209
210             PrefsDialog.getStore().setValue(PREFS_FILTERS, sb.toString());
211         }
212
213         public boolean requiresDefaultFilter() {
214             return true;
215         }
216     }
217
218
219     private LogPanel mLogPanel;
220
221     private ToolItemAction mCreateFilterAction;
222     private ToolItemAction mDeleteFilterAction;
223     private ToolItemAction mEditFilterAction;
224     private ToolItemAction mExportAction;
225     private ToolItemAction mClearAction;
226
227     private ToolItemAction[] mLogLevelActions;
228     private String[] mLogLevelIcons = {
229             "v.png", //$NON-NLS-1S
230             "d.png", //$NON-NLS-1S
231             "i.png", //$NON-NLS-1S
232             "w.png", //$NON-NLS-1S
233             "e.png", //$NON-NLS-1S
234     };
235
236     protected Clipboard mClipboard;
237
238     private MenuItem mCopyMenuItem;
239
240     private MenuItem mSelectAllMenuItem;
241
242     private TableFocusListener mTableListener;
243
244     private DeviceExplorer mExplorer = null;
245     private Shell mExplorerShell = null;
246
247     private EmulatorControlPanel mEmulatorPanel;
248
249     private EventLogPanel mEventLogPanel;
250
251     private Image mTracingStartImage;
252
253     private Image mTracingStopImage;
254
255     private ImageLoader mDdmUiLibLoader;
256
257     private class TableFocusListener implements ITableFocusListener {
258
259         private IFocusedTableActivator mCurrentActivator;
260
261         public void focusGained(IFocusedTableActivator activator) {
262             mCurrentActivator = activator;
263             if (mCopyMenuItem.isDisposed() == false) {
264                 mCopyMenuItem.setEnabled(true);
265                 mSelectAllMenuItem.setEnabled(true);
266             }
267         }
268
269         public void focusLost(IFocusedTableActivator activator) {
270             // if we move from one table to another, it's unclear
271             // if the old table lose its focus before the new
272             // one gets the focus, so we need to check.
273             if (activator == mCurrentActivator) {
274                 activator = null;
275                 if (mCopyMenuItem.isDisposed() == false) {
276                     mCopyMenuItem.setEnabled(false);
277                     mSelectAllMenuItem.setEnabled(false);
278                 }
279             }
280         }
281
282         public void copy(Clipboard clipboard) {
283             if (mCurrentActivator != null) {
284                 mCurrentActivator.copy(clipboard);
285             }
286         }
287
288         public void selectAll() {
289             if (mCurrentActivator != null) {
290                 mCurrentActivator.selectAll();
291             }
292         }
293     }
294
295     /**
296      * Handler for HPROF dumps.
297      * This will always prompt the user to save the HPROF file.
298      */
299     private class HProfHandler extends BaseFileHandler implements IHprofDumpHandler {
300
301         public HProfHandler(Shell parentShell) {
302             super(parentShell);
303         }
304
305         public void onEndFailure(final Client client, final String message) {
306             mDisplay.asyncExec(new Runnable() {
307                 public void run() {
308                     try {
309                         displayErrorFromUiThread(
310                                 "Unable to create HPROF file for application '%1$s'\n\n%2$s" +
311                                 "Check logcat for more information.",
312                                 client.getClientData().getClientDescription(),
313                                 message != null ? message + "\n\n" : "");
314                     } finally {
315                         // this will make sure the dump hprof button is re-enabled for the
316                         // current selection. as the client is finished dumping an hprof file
317                         enableButtons();
318                     }
319                 }
320             });
321         }
322
323         public void onSuccess(final String remoteFilePath, final Client client) {
324             mDisplay.asyncExec(new Runnable() {
325                 public void run() {
326                     final IDevice device = client.getDevice();
327                     try {
328                         // get the sync service to pull the HPROF file
329                         final SyncService sync = client.getDevice().getSyncService();
330                         if (sync != null) {
331                             promptAndPull(sync,
332                                     client.getClientData().getClientDescription() + ".hprof",
333                                     remoteFilePath, "Save HPROF file");
334                         } else {
335                             displayErrorFromUiThread(
336                                     "Unable to download HPROF file from device '%1$s'.",
337                                     device.getSerialNumber());
338                         }
339                     } catch (SyncException e) {
340                         if (e.wasCanceled() == false) {
341                             displayErrorFromUiThread(
342                                     "Unable to download HPROF file from device '%1$s'.\n\n%2$s",
343                                     device.getSerialNumber(), e.getMessage());
344                         }
345                     } catch (Exception e) {
346                         displayErrorFromUiThread("Unable to download HPROF file from device '%1$s'.",
347                                 device.getSerialNumber());
348
349                     } finally {
350                         // this will make sure the dump hprof button is re-enabled for the
351                         // current selection. as the client is finished dumping an hprof file
352                         enableButtons();
353                     }
354                 }
355             });
356         }
357
358         public void onSuccess(final byte[] data, final Client client) {
359             mDisplay.asyncExec(new Runnable() {
360                 public void run() {
361                     promptAndSave(client.getClientData().getClientDescription() + ".hprof", data,
362                             "Save HPROF file");
363                 }
364             });
365         }
366
367         @Override
368         protected String getDialogTitle() {
369             return "HPROF Error";
370         }
371     }
372
373
374     /**
375      * Generic constructor.
376      */
377     private UIThread() {
378         mPanels[PANEL_INFO] = new InfoPanel();
379         mPanels[PANEL_THREAD] = new ThreadPanel();
380         mPanels[PANEL_HEAP] = new HeapPanel();
381         if (PrefsDialog.getStore().getBoolean(PrefsDialog.SHOW_NATIVE_HEAP)) {
382             mPanels[PANEL_NATIVE_HEAP] = new NativeHeapPanel();
383         } else {
384             mPanels[PANEL_NATIVE_HEAP] = null;
385         }
386         mPanels[PANEL_ALLOCATIONS] = new AllocationPanel();
387         mPanels[PANEL_SYSINFO] = new SysinfoPanel();
388     }
389
390     /**
391      * Get singleton instance of the UI thread.
392      */
393     public static UIThread getInstance() {
394         return mInstance;
395     }
396
397     /**
398      * Return the Display. Don't try this unless you're in the UI thread.
399      */
400     public Display getDisplay() {
401         return mDisplay;
402     }
403
404     public void asyncExec(Runnable r) {
405         if (mDisplay != null && mDisplay.isDisposed() == false) {
406             mDisplay.asyncExec(r);
407         }
408     }
409
410     /** returns the IPreferenceStore */
411     public IPreferenceStore getStore() {
412         return PrefsDialog.getStore();
413     }
414
415     /**
416      * Create SWT objects and drive the user interface event loop.
417      * @param location location of the folder that contains ddms.
418      */
419     public void runUI(String ddmsParentLocation) {
420         Display.setAppName("ddms");
421         mDisplay = new Display();
422         final Shell shell = new Shell(mDisplay);
423
424         // create the image loaders for DDMS and DDMUILIB
425         mDdmUiLibLoader = ImageLoader.getDdmUiLibLoader();
426
427         shell.setImage(ImageLoader.getLoader(this.getClass()).loadImage(mDisplay,
428                 "ddms-icon.png", //$NON-NLS-1$
429                 100, 50, null));
430
431         Log.setLogOutput(new ILogOutput() {
432             public void printAndPromptLog(final LogLevel logLevel, final String tag,
433                     final String message) {
434                 Log.printLog(logLevel, tag, message);
435                 // dialog box only run in UI thread..
436                 mDisplay.asyncExec(new Runnable() {
437                     public void run() {
438                         Shell shell = mDisplay.getActiveShell();
439                         if (logLevel == LogLevel.ERROR) {
440                             MessageDialog.openError(shell, tag, message);
441                         } else {
442                             MessageDialog.openWarning(shell, tag, message);
443                         }
444                     }
445                 });
446             }
447
448             public void printLog(LogLevel logLevel, String tag, String message) {
449                 Log.printLog(logLevel, tag, message);
450             }
451         });
452
453         // set the handler for hprof dump
454         ClientData.setHprofDumpHandler(new HProfHandler(shell));
455         ClientData.setMethodProfilingHandler(new MethodProfilingHandler(shell));
456
457         // [try to] ensure ADB is running
458         // in the new SDK, adb is in the platform-tools, but when run from the command line
459         // in the Android source tree, then adb is next to ddms.
460         String adbLocation;
461         if (ddmsParentLocation != null && ddmsParentLocation.length() != 0) {
462             // check if there's a platform-tools folder
463             File platformTools = new File(new File(ddmsParentLocation).getParent(),
464                     "platform-tools");  //$NON-NLS-1$
465             if (platformTools.isDirectory()) {
466                 adbLocation = platformTools.getAbsolutePath() + File.separator + "adb"; //$NON-NLS-1$
467             } else {
468                 adbLocation = ddmsParentLocation + File.separator + "adb"; //$NON-NLS-1$
469             }
470         } else {
471             adbLocation = "adb"; //$NON-NLS-1$
472         }
473
474         AndroidDebugBridge.init(true /* debugger support */);
475         AndroidDebugBridge.createBridge(adbLocation, true /* forceNewBridge */);
476
477         // we need to listen to client change to be notified of client status (profiling) change
478         AndroidDebugBridge.addClientChangeListener(this);
479
480         shell.setText("Dalvik Debug Monitor");
481         setConfirmClose(shell);
482         createMenus(shell);
483         createWidgets(shell);
484
485         shell.pack();
486         setSizeAndPosition(shell);
487         shell.open();
488
489         Log.d("ddms", "UI is up");
490
491         while (!shell.isDisposed()) {
492             if (!mDisplay.readAndDispatch())
493                 mDisplay.sleep();
494         }
495         mLogPanel.stopLogCat(true);
496
497         mDevicePanel.dispose();
498         for (TablePanel panel : mPanels) {
499             if (panel != null) {
500                 panel.dispose();
501             }
502         }
503
504         ImageLoader.dispose();
505
506         mDisplay.dispose();
507         Log.d("ddms", "UI is down");
508     }
509
510     /**
511      * Set the size and position of the main window from the preference, and
512      * setup listeners for control events (resize/move of the window)
513      */
514     private void setSizeAndPosition(final Shell shell) {
515         shell.setMinimumSize(400, 200);
516
517         // get the x/y and w/h from the prefs
518         PreferenceStore prefs = PrefsDialog.getStore();
519         int x = prefs.getInt(PrefsDialog.SHELL_X);
520         int y = prefs.getInt(PrefsDialog.SHELL_Y);
521         int w = prefs.getInt(PrefsDialog.SHELL_WIDTH);
522         int h = prefs.getInt(PrefsDialog.SHELL_HEIGHT);
523
524         // check that we're not out of the display area
525         Rectangle rect = mDisplay.getClientArea();
526         // first check the width/height
527         if (w > rect.width) {
528             w = rect.width;
529             prefs.setValue(PrefsDialog.SHELL_WIDTH, rect.width);
530         }
531         if (h > rect.height) {
532             h = rect.height;
533             prefs.setValue(PrefsDialog.SHELL_HEIGHT, rect.height);
534         }
535         // then check x. Make sure the left corner is in the screen
536         if (x < rect.x) {
537             x = rect.x;
538             prefs.setValue(PrefsDialog.SHELL_X, rect.x);
539         } else if (x >= rect.x + rect.width) {
540             x = rect.x + rect.width - w;
541             prefs.setValue(PrefsDialog.SHELL_X, rect.x);
542         }
543         // then check y. Make sure the left corner is in the screen
544         if (y < rect.y) {
545             y = rect.y;
546             prefs.setValue(PrefsDialog.SHELL_Y, rect.y);
547         } else if (y >= rect.y + rect.height) {
548             y = rect.y + rect.height - h;
549             prefs.setValue(PrefsDialog.SHELL_Y, rect.y);
550         }
551
552         // now we can set the location/size
553         shell.setBounds(x, y, w, h);
554
555         // add listener for resize/move
556         shell.addControlListener(new ControlListener() {
557             public void controlMoved(ControlEvent e) {
558                 // get the new x/y
559                 Rectangle rect = shell.getBounds();
560                 // store in pref file
561                 PreferenceStore prefs = PrefsDialog.getStore();
562                 prefs.setValue(PrefsDialog.SHELL_X, rect.x);
563                 prefs.setValue(PrefsDialog.SHELL_Y, rect.y);
564             }
565
566             public void controlResized(ControlEvent e) {
567                 // get the new w/h
568                 Rectangle rect = shell.getBounds();
569                 // store in pref file
570                 PreferenceStore prefs = PrefsDialog.getStore();
571                 prefs.setValue(PrefsDialog.SHELL_WIDTH, rect.width);
572                 prefs.setValue(PrefsDialog.SHELL_HEIGHT, rect.height);
573             }
574         });
575     }
576
577     /**
578      * Set the size and position of the file explorer window from the
579      * preference, and setup listeners for control events (resize/move of
580      * the window)
581      */
582     private void setExplorerSizeAndPosition(final Shell shell) {
583         shell.setMinimumSize(400, 200);
584
585         // get the x/y and w/h from the prefs
586         PreferenceStore prefs = PrefsDialog.getStore();
587         int x = prefs.getInt(PrefsDialog.EXPLORER_SHELL_X);
588         int y = prefs.getInt(PrefsDialog.EXPLORER_SHELL_Y);
589         int w = prefs.getInt(PrefsDialog.EXPLORER_SHELL_WIDTH);
590         int h = prefs.getInt(PrefsDialog.EXPLORER_SHELL_HEIGHT);
591
592         // check that we're not out of the display area
593         Rectangle rect = mDisplay.getClientArea();
594         // first check the width/height
595         if (w > rect.width) {
596             w = rect.width;
597             prefs.setValue(PrefsDialog.EXPLORER_SHELL_WIDTH, rect.width);
598         }
599         if (h > rect.height) {
600             h = rect.height;
601             prefs.setValue(PrefsDialog.EXPLORER_SHELL_HEIGHT, rect.height);
602         }
603         // then check x. Make sure the left corner is in the screen
604         if (x < rect.x) {
605             x = rect.x;
606             prefs.setValue(PrefsDialog.EXPLORER_SHELL_X, rect.x);
607         } else if (x >= rect.x + rect.width) {
608             x = rect.x + rect.width - w;
609             prefs.setValue(PrefsDialog.EXPLORER_SHELL_X, rect.x);
610         }
611         // then check y. Make sure the left corner is in the screen
612         if (y < rect.y) {
613             y = rect.y;
614             prefs.setValue(PrefsDialog.EXPLORER_SHELL_Y, rect.y);
615         } else if (y >= rect.y + rect.height) {
616             y = rect.y + rect.height - h;
617             prefs.setValue(PrefsDialog.EXPLORER_SHELL_Y, rect.y);
618         }
619
620         // now we can set the location/size
621         shell.setBounds(x, y, w, h);
622
623         // add listener for resize/move
624         shell.addControlListener(new ControlListener() {
625             public void controlMoved(ControlEvent e) {
626                 // get the new x/y
627                 Rectangle rect = shell.getBounds();
628                 // store in pref file
629                 PreferenceStore prefs = PrefsDialog.getStore();
630                 prefs.setValue(PrefsDialog.EXPLORER_SHELL_X, rect.x);
631                 prefs.setValue(PrefsDialog.EXPLORER_SHELL_Y, rect.y);
632             }
633
634             public void controlResized(ControlEvent e) {
635                 // get the new w/h
636                 Rectangle rect = shell.getBounds();
637                 // store in pref file
638                 PreferenceStore prefs = PrefsDialog.getStore();
639                 prefs.setValue(PrefsDialog.EXPLORER_SHELL_WIDTH, rect.width);
640                 prefs.setValue(PrefsDialog.EXPLORER_SHELL_HEIGHT, rect.height);
641             }
642         });
643     }
644
645     /*
646      * Set the confirm-before-close dialog. TODO: enable/disable in prefs. TODO:
647      * is there any point in having this?
648      */
649     private void setConfirmClose(final Shell shell) {
650         if (true)
651             return;
652
653         shell.addListener(SWT.Close, new Listener() {
654             public void handleEvent(Event event) {
655                 int style = SWT.APPLICATION_MODAL | SWT.YES | SWT.NO;
656                 MessageBox msgBox = new MessageBox(shell, style);
657                 msgBox.setText("Confirm...");
658                 msgBox.setMessage("Close DDM?");
659                 event.doit = (msgBox.open() == SWT.YES);
660             }
661         });
662     }
663
664     /*
665      * Create the menu bar and items.
666      */
667     private void createMenus(final Shell shell) {
668         // create menu bar
669         Menu menuBar = new Menu(shell, SWT.BAR);
670
671         // create top-level items
672         MenuItem fileItem = new MenuItem(menuBar, SWT.CASCADE);
673         fileItem.setText("&File");
674         MenuItem editItem = new MenuItem(menuBar, SWT.CASCADE);
675         editItem.setText("&Edit");
676         MenuItem actionItem = new MenuItem(menuBar, SWT.CASCADE);
677         actionItem.setText("&Actions");
678         MenuItem deviceItem = new MenuItem(menuBar, SWT.CASCADE);
679         deviceItem.setText("&Device");
680         MenuItem helpItem = new MenuItem(menuBar, SWT.CASCADE);
681         helpItem.setText("&Help");
682
683         // create top-level menus
684         Menu fileMenu = new Menu(menuBar);
685         fileItem.setMenu(fileMenu);
686         Menu editMenu = new Menu(menuBar);
687         editItem.setMenu(editMenu);
688         Menu actionMenu = new Menu(menuBar);
689         actionItem.setMenu(actionMenu);
690         Menu deviceMenu = new Menu(menuBar);
691         deviceItem.setMenu(deviceMenu);
692         Menu helpMenu = new Menu(menuBar);
693         helpItem.setMenu(helpMenu);
694
695         MenuItem item;
696
697         // create File menu items
698         item = new MenuItem(fileMenu, SWT.NONE);
699         item.setText("&Preferences...");
700         item.addSelectionListener(new SelectionAdapter() {
701             @Override
702             public void widgetSelected(SelectionEvent e) {
703                 PrefsDialog.run(shell);
704             }
705         });
706
707         item = new MenuItem(fileMenu, SWT.NONE);
708         item.setText("&Static Port Configuration...");
709         item.addSelectionListener(new SelectionAdapter() {
710             @Override
711             public void widgetSelected(SelectionEvent e) {
712                 StaticPortConfigDialog dlg = new StaticPortConfigDialog(shell);
713                 dlg.open();
714             }
715         });
716
717         new MenuItem(fileMenu, SWT.SEPARATOR);
718
719         item = new MenuItem(fileMenu, SWT.NONE);
720         item.setText("E&xit\tCtrl-Q");
721         item.setAccelerator('Q' | SWT.CONTROL);
722         item.addSelectionListener(new SelectionAdapter() {
723             @Override
724             public void widgetSelected(SelectionEvent e) {
725                 shell.close();
726             }
727         });
728
729         // create edit menu items
730         mCopyMenuItem = new MenuItem(editMenu, SWT.NONE);
731         mCopyMenuItem.setText("&Copy\tCtrl-C");
732         mCopyMenuItem.setAccelerator('C' | SWT.COMMAND);
733         mCopyMenuItem.addSelectionListener(new SelectionAdapter() {
734             @Override
735             public void widgetSelected(SelectionEvent e) {
736                 mTableListener.copy(mClipboard);
737             }
738         });
739
740         new MenuItem(editMenu, SWT.SEPARATOR);
741
742         mSelectAllMenuItem = new MenuItem(editMenu, SWT.NONE);
743         mSelectAllMenuItem.setText("Select &All\tCtrl-A");
744         mSelectAllMenuItem.setAccelerator('A' | SWT.COMMAND);
745         mSelectAllMenuItem.addSelectionListener(new SelectionAdapter() {
746             @Override
747             public void widgetSelected(SelectionEvent e) {
748                 mTableListener.selectAll();
749             }
750         });
751
752         // create Action menu items
753         // TODO: this should come with a confirmation dialog
754         final MenuItem actionHaltItem = new MenuItem(actionMenu, SWT.NONE);
755         actionHaltItem.setText("&Halt VM");
756         actionHaltItem.addSelectionListener(new SelectionAdapter() {
757             @Override
758             public void widgetSelected(SelectionEvent e) {
759                 mDevicePanel.killSelectedClient();
760             }
761         });
762
763         final MenuItem actionCauseGcItem = new MenuItem(actionMenu, SWT.NONE);
764         actionCauseGcItem.setText("Cause &GC");
765         actionCauseGcItem.addSelectionListener(new SelectionAdapter() {
766             @Override
767             public void widgetSelected(SelectionEvent e) {
768                 mDevicePanel.forceGcOnSelectedClient();
769             }
770         });
771
772         // configure Action items based on current state
773         actionMenu.addMenuListener(new MenuAdapter() {
774             @Override
775             public void menuShown(MenuEvent e) {
776                 actionHaltItem.setEnabled(mTBHalt.getEnabled() && mCurrentClient != null);
777                 actionCauseGcItem.setEnabled(mTBCauseGc.getEnabled() && mCurrentClient != null);
778             }
779         });
780
781         // create Device menu items
782         final MenuItem screenShotItem = new MenuItem(deviceMenu, SWT.NONE);
783         screenShotItem.setText("&Screen capture...\tCtrl-S");
784         screenShotItem.setAccelerator('S' | SWT.CONTROL);
785         screenShotItem.addSelectionListener(new SelectionAdapter() {
786             @Override
787             public void widgetSelected(SelectionEvent e) {
788                 if (mCurrentDevice != null) {
789                     ScreenShotDialog dlg = new ScreenShotDialog(shell);
790                     dlg.open(mCurrentDevice);
791                 }
792             }
793         });
794
795         new MenuItem(deviceMenu, SWT.SEPARATOR);
796
797         final MenuItem explorerItem = new MenuItem(deviceMenu, SWT.NONE);
798         explorerItem.setText("File Explorer...");
799         explorerItem.addSelectionListener(new SelectionAdapter() {
800             @Override
801             public void widgetSelected(SelectionEvent e) {
802                 createFileExplorer();
803             }
804         });
805
806         new MenuItem(deviceMenu, SWT.SEPARATOR);
807
808         final MenuItem processItem = new MenuItem(deviceMenu, SWT.NONE);
809         processItem.setText("Show &process status...");
810         processItem.addSelectionListener(new SelectionAdapter() {
811             @Override
812             public void widgetSelected(SelectionEvent e) {
813                 DeviceCommandDialog dlg;
814                 dlg = new DeviceCommandDialog("ps -x", "ps-x.txt", shell);
815                 dlg.open(mCurrentDevice);
816             }
817         });
818
819         final MenuItem deviceStateItem = new MenuItem(deviceMenu, SWT.NONE);
820         deviceStateItem.setText("Dump &device state...");
821         deviceStateItem.addSelectionListener(new SelectionAdapter() {
822             @Override
823             public void widgetSelected(SelectionEvent e) {
824                 DeviceCommandDialog dlg;
825                 dlg = new DeviceCommandDialog("/system/bin/dumpstate /proc/self/fd/0",
826                         "device-state.txt", shell);
827                 dlg.open(mCurrentDevice);
828             }
829         });
830
831         final MenuItem appStateItem = new MenuItem(deviceMenu, SWT.NONE);
832         appStateItem.setText("Dump &app state...");
833         appStateItem.setEnabled(false);
834         appStateItem.addSelectionListener(new SelectionAdapter() {
835             @Override
836             public void widgetSelected(SelectionEvent e) {
837                 DeviceCommandDialog dlg;
838                 dlg = new DeviceCommandDialog("dumpsys", "app-state.txt", shell);
839                 dlg.open(mCurrentDevice);
840             }
841         });
842
843         final MenuItem radioStateItem = new MenuItem(deviceMenu, SWT.NONE);
844         radioStateItem.setText("Dump &radio state...");
845         radioStateItem.addSelectionListener(new SelectionAdapter() {
846             @Override
847             public void widgetSelected(SelectionEvent e) {
848                 DeviceCommandDialog dlg;
849                 dlg = new DeviceCommandDialog(
850                         "cat /data/logs/radio.4 /data/logs/radio.3"
851                         + " /data/logs/radio.2 /data/logs/radio.1"
852                         + " /data/logs/radio",
853                         "radio-state.txt", shell);
854                 dlg.open(mCurrentDevice);
855             }
856         });
857
858         final MenuItem logCatItem = new MenuItem(deviceMenu, SWT.NONE);
859         logCatItem.setText("Run &logcat...");
860         logCatItem.addSelectionListener(new SelectionAdapter() {
861             @Override
862             public void widgetSelected(SelectionEvent e) {
863                 DeviceCommandDialog dlg;
864                 dlg = new DeviceCommandDialog("logcat '*:d jdwp:w'", "log.txt",
865                         shell);
866                 dlg.open(mCurrentDevice);
867             }
868         });
869
870         // configure Action items based on current state
871         deviceMenu.addMenuListener(new MenuAdapter() {
872             @Override
873             public void menuShown(MenuEvent e) {
874                 boolean deviceEnabled = mCurrentDevice != null;
875                 screenShotItem.setEnabled(deviceEnabled);
876                 explorerItem.setEnabled(deviceEnabled);
877                 processItem.setEnabled(deviceEnabled);
878                 deviceStateItem.setEnabled(deviceEnabled);
879                 appStateItem.setEnabled(deviceEnabled);
880                 radioStateItem.setEnabled(deviceEnabled);
881                 logCatItem.setEnabled(deviceEnabled);
882             }
883         });
884
885         // create Help menu items
886         item = new MenuItem(helpMenu, SWT.NONE);
887         item.setText("&Contents...");
888         item.addSelectionListener(new SelectionAdapter() {
889             @Override
890             public void widgetSelected(SelectionEvent e) {
891                 int style = SWT.APPLICATION_MODAL | SWT.OK;
892                 MessageBox msgBox = new MessageBox(shell, style);
893                 msgBox.setText("Help!");
894                 msgBox.setMessage("Help wanted.");
895                 msgBox.open();
896             }
897         });
898
899         new MenuItem(helpMenu, SWT.SEPARATOR);
900
901         item = new MenuItem(helpMenu, SWT.NONE);
902         item.setText("&About...");
903         item.addSelectionListener(new SelectionAdapter() {
904             @Override
905             public void widgetSelected(SelectionEvent e) {
906                 AboutDialog dlg = new AboutDialog(shell);
907                 dlg.open();
908             }
909         });
910
911         // tell the shell to use this menu
912         shell.setMenuBar(menuBar);
913     }
914
915     /*
916      * Create the widgets in the main application window. The basic layout is a
917      * two-panel sash, with a scrolling list of VMs on the left and detailed
918      * output for a single VM on the right.
919      */
920     private void createWidgets(final Shell shell) {
921         Color darkGray = shell.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY);
922
923         /*
924          * Create three areas: tool bar, split panels, status line
925          */
926         shell.setLayout(new GridLayout(1, false));
927
928         // 1. panel area
929         final Composite panelArea = new Composite(shell, SWT.BORDER);
930
931         // make the panel area absorb all space
932         panelArea.setLayoutData(new GridData(GridData.FILL_BOTH));
933
934         // 2. status line.
935         mStatusLine = new Label(shell, SWT.NONE);
936
937         // make status line extend all the way across
938         mStatusLine.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
939
940         mStatusLine.setText("Initializing...");
941
942         /*
943          * Configure the split-panel area.
944          */
945         final PreferenceStore prefs = PrefsDialog.getStore();
946
947         Composite topPanel = new Composite(panelArea, SWT.NONE);
948         final Sash sash = new Sash(panelArea, SWT.HORIZONTAL);
949         sash.setBackground(darkGray);
950         Composite bottomPanel = new Composite(panelArea, SWT.NONE);
951
952         panelArea.setLayout(new FormLayout());
953
954         createTopPanel(topPanel, darkGray);
955         createBottomPanel(bottomPanel);
956
957         // form layout data
958         FormData data = new FormData();
959         data.top = new FormAttachment(0, 0);
960         data.bottom = new FormAttachment(sash, 0);
961         data.left = new FormAttachment(0, 0);
962         data.right = new FormAttachment(100, 0);
963         topPanel.setLayoutData(data);
964
965         final FormData sashData = new FormData();
966         if (prefs != null && prefs.contains(PREFERENCE_LOGSASH)) {
967             sashData.top = new FormAttachment(0, prefs.getInt(
968                     PREFERENCE_LOGSASH));
969         } else {
970             sashData.top = new FormAttachment(50,0); // 50% across
971         }
972         sashData.left = new FormAttachment(0, 0);
973         sashData.right = new FormAttachment(100, 0);
974         sash.setLayoutData(sashData);
975
976         data = new FormData();
977         data.top = new FormAttachment(sash, 0);
978         data.bottom = new FormAttachment(100, 0);
979         data.left = new FormAttachment(0, 0);
980         data.right = new FormAttachment(100, 0);
981         bottomPanel.setLayoutData(data);
982
983         // allow resizes, but cap at minPanelWidth
984         sash.addListener(SWT.Selection, new Listener() {
985             public void handleEvent(Event e) {
986                 Rectangle sashRect = sash.getBounds();
987                 Rectangle panelRect = panelArea.getClientArea();
988                 int bottom = panelRect.height - sashRect.height - 100;
989                 e.y = Math.max(Math.min(e.y, bottom), 100);
990                 if (e.y != sashRect.y) {
991                     sashData.top = new FormAttachment(0, e.y);
992                     prefs.setValue(PREFERENCE_LOGSASH, e.y);
993                     panelArea.layout();
994                 }
995             }
996         });
997
998         // add a global focus listener for all the tables
999         mTableListener = new TableFocusListener();
1000
1001         // now set up the listener in the various panels
1002         mLogPanel.setTableFocusListener(mTableListener);
1003         mEventLogPanel.setTableFocusListener(mTableListener);
1004         for (TablePanel p : mPanels) {
1005             if (p != null) {
1006                 p.setTableFocusListener(mTableListener);
1007             }
1008         }
1009
1010         mStatusLine.setText("");
1011     }
1012
1013     /*
1014      * Populate the tool bar.
1015      */
1016     private void createDevicePanelToolBar(ToolBar toolBar) {
1017         Display display = toolBar.getDisplay();
1018
1019         // add "show heap updates" button
1020         mTBShowHeapUpdates = new ToolItem(toolBar, SWT.CHECK);
1021         mTBShowHeapUpdates.setImage(mDdmUiLibLoader.loadImage(display,
1022                 DevicePanel.ICON_HEAP, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1023         mTBShowHeapUpdates.setToolTipText("Show heap updates");
1024         mTBShowHeapUpdates.setEnabled(false);
1025         mTBShowHeapUpdates.addSelectionListener(new SelectionAdapter() {
1026             @Override
1027             public void widgetSelected(SelectionEvent e) {
1028                 if (mCurrentClient != null) {
1029                     // boolean status = ((ToolItem)e.item).getSelection();
1030                     // invert previous state
1031                     boolean enable = !mCurrentClient.isHeapUpdateEnabled();
1032                     mCurrentClient.setHeapUpdateEnabled(enable);
1033                 } else {
1034                     e.doit = false; // this has no effect?
1035                 }
1036             }
1037         });
1038
1039         // add "dump HPROF" button
1040         mTBDumpHprof = new ToolItem(toolBar, SWT.PUSH);
1041         mTBDumpHprof.setToolTipText("Dump HPROF file");
1042         mTBDumpHprof.setEnabled(false);
1043         mTBDumpHprof.setImage(mDdmUiLibLoader.loadImage(display,
1044                 DevicePanel.ICON_HPROF, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1045         mTBDumpHprof.addSelectionListener(new SelectionAdapter() {
1046             @Override
1047             public void widgetSelected(SelectionEvent e) {
1048                 mDevicePanel.dumpHprof();
1049
1050                 // this will make sure the dump hprof button is disabled for the current selection
1051                 // as the client is already dumping an hprof file
1052                 enableButtons();
1053             }
1054         });
1055
1056         // add "cause GC" button
1057         mTBCauseGc = new ToolItem(toolBar, SWT.PUSH);
1058         mTBCauseGc.setToolTipText("Cause an immediate GC");
1059         mTBCauseGc.setEnabled(false);
1060         mTBCauseGc.setImage(mDdmUiLibLoader.loadImage(display,
1061                 DevicePanel.ICON_GC, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1062         mTBCauseGc.addSelectionListener(new SelectionAdapter() {
1063             @Override
1064             public void widgetSelected(SelectionEvent e) {
1065                 mDevicePanel.forceGcOnSelectedClient();
1066             }
1067         });
1068
1069         new ToolItem(toolBar, SWT.SEPARATOR);
1070
1071         // add "show thread updates" button
1072         mTBShowThreadUpdates = new ToolItem(toolBar, SWT.CHECK);
1073         mTBShowThreadUpdates.setImage(mDdmUiLibLoader.loadImage(display,
1074                 DevicePanel.ICON_THREAD, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1075         mTBShowThreadUpdates.setToolTipText("Show thread updates");
1076         mTBShowThreadUpdates.setEnabled(false);
1077         mTBShowThreadUpdates.addSelectionListener(new SelectionAdapter() {
1078             @Override
1079             public void widgetSelected(SelectionEvent e) {
1080                 if (mCurrentClient != null) {
1081                     // boolean status = ((ToolItem)e.item).getSelection();
1082                     // invert previous state
1083                     boolean enable = !mCurrentClient.isThreadUpdateEnabled();
1084
1085                     mCurrentClient.setThreadUpdateEnabled(enable);
1086                 } else {
1087                     e.doit = false; // this has no effect?
1088                 }
1089             }
1090         });
1091
1092         // add a start/stop method tracing
1093         mTracingStartImage = mDdmUiLibLoader.loadImage(display,
1094                 DevicePanel.ICON_TRACING_START,
1095                 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null);
1096         mTracingStopImage = mDdmUiLibLoader.loadImage(display,
1097                 DevicePanel.ICON_TRACING_STOP,
1098                 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null);
1099         mTBProfiling = new ToolItem(toolBar, SWT.PUSH);
1100         mTBProfiling.setToolTipText("Start Method Profiling");
1101         mTBProfiling.setEnabled(false);
1102         mTBProfiling.setImage(mTracingStartImage);
1103         mTBProfiling.addSelectionListener(new SelectionAdapter() {
1104             @Override
1105             public void widgetSelected(SelectionEvent e) {
1106                 mDevicePanel.toggleMethodProfiling();
1107             }
1108         });
1109
1110         new ToolItem(toolBar, SWT.SEPARATOR);
1111
1112         // add "kill VM" button; need to make this visually distinct from
1113         // the status update buttons
1114         mTBHalt = new ToolItem(toolBar, SWT.PUSH);
1115         mTBHalt.setToolTipText("Halt the target VM");
1116         mTBHalt.setEnabled(false);
1117         mTBHalt.setImage(mDdmUiLibLoader.loadImage(display,
1118                 DevicePanel.ICON_HALT, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1119         mTBHalt.addSelectionListener(new SelectionAdapter() {
1120             @Override
1121             public void widgetSelected(SelectionEvent e) {
1122                 mDevicePanel.killSelectedClient();
1123             }
1124         });
1125
1126        toolBar.pack();
1127     }
1128
1129     private void createTopPanel(final Composite comp, Color darkGray) {
1130         final PreferenceStore prefs = PrefsDialog.getStore();
1131
1132         comp.setLayout(new FormLayout());
1133
1134         Composite leftPanel = new Composite(comp, SWT.NONE);
1135         final Sash sash = new Sash(comp, SWT.VERTICAL);
1136         sash.setBackground(darkGray);
1137         Composite rightPanel = new Composite(comp, SWT.NONE);
1138
1139         createLeftPanel(leftPanel);
1140         createRightPanel(rightPanel);
1141
1142         FormData data = new FormData();
1143         data.top = new FormAttachment(0, 0);
1144         data.bottom = new FormAttachment(100, 0);
1145         data.left = new FormAttachment(0, 0);
1146         data.right = new FormAttachment(sash, 0);
1147         leftPanel.setLayoutData(data);
1148
1149         final FormData sashData = new FormData();
1150         sashData.top = new FormAttachment(0, 0);
1151         sashData.bottom = new FormAttachment(100, 0);
1152         if (prefs != null && prefs.contains(PREFERENCE_SASH)) {
1153             sashData.left = new FormAttachment(0, prefs.getInt(
1154                     PREFERENCE_SASH));
1155         } else {
1156             // position the sash 380 from the right instead of x% (done by using
1157             // FormAttachment(x, 0)) in order to keep the sash at the same
1158             // position
1159             // from the left when the window is resized.
1160             // 380px is just enough to display the left table with no horizontal
1161             // scrollbar with the default font.
1162             sashData.left = new FormAttachment(0, 380);
1163         }
1164         sash.setLayoutData(sashData);
1165
1166         data = new FormData();
1167         data.top = new FormAttachment(0, 0);
1168         data.bottom = new FormAttachment(100, 0);
1169         data.left = new FormAttachment(sash, 0);
1170         data.right = new FormAttachment(100, 0);
1171         rightPanel.setLayoutData(data);
1172
1173         final int minPanelWidth = 60;
1174
1175         // allow resizes, but cap at minPanelWidth
1176         sash.addListener(SWT.Selection, new Listener() {
1177             public void handleEvent(Event e) {
1178                 Rectangle sashRect = sash.getBounds();
1179                 Rectangle panelRect = comp.getClientArea();
1180                 int right = panelRect.width - sashRect.width - minPanelWidth;
1181                 e.x = Math.max(Math.min(e.x, right), minPanelWidth);
1182                 if (e.x != sashRect.x) {
1183                     sashData.left = new FormAttachment(0, e.x);
1184                     prefs.setValue(PREFERENCE_SASH, e.x);
1185                     comp.layout();
1186                 }
1187             }
1188         });
1189     }
1190
1191     private void createBottomPanel(final Composite comp) {
1192         final PreferenceStore prefs = PrefsDialog.getStore();
1193
1194         // create clipboard
1195         Display display = comp.getDisplay();
1196         mClipboard = new Clipboard(display);
1197
1198         LogColors colors = new LogColors();
1199
1200         colors.infoColor = new Color(display, 0, 127, 0);
1201         colors.debugColor = new Color(display, 0, 0, 127);
1202         colors.errorColor = new Color(display, 255, 0, 0);
1203         colors.warningColor = new Color(display, 255, 127, 0);
1204         colors.verboseColor = new Color(display, 0, 0, 0);
1205
1206         // set the preferences names
1207         LogPanel.PREFS_TIME = PREFS_COL_TIME;
1208         LogPanel.PREFS_LEVEL = PREFS_COL_LEVEL;
1209         LogPanel.PREFS_PID = PREFS_COL_PID;
1210         LogPanel.PREFS_TAG = PREFS_COL_TAG;
1211         LogPanel.PREFS_MESSAGE = PREFS_COL_MESSAGE;
1212
1213         comp.setLayout(new GridLayout(1, false));
1214
1215         ToolBar toolBar = new ToolBar(comp, SWT.HORIZONTAL);
1216
1217         mCreateFilterAction = new ToolItemAction(toolBar, SWT.PUSH);
1218         mCreateFilterAction.item.setToolTipText("Create Filter");
1219         mCreateFilterAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay,
1220                 "add.png", //$NON-NLS-1$
1221                 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1222         mCreateFilterAction.item.addSelectionListener(new SelectionAdapter() {
1223             @Override
1224             public void widgetSelected(SelectionEvent e) {
1225                 mLogPanel.addFilter();
1226             }
1227         });
1228
1229         mEditFilterAction = new ToolItemAction(toolBar, SWT.PUSH);
1230         mEditFilterAction.item.setToolTipText("Edit Filter");
1231         mEditFilterAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay,
1232                 "edit.png", //$NON-NLS-1$
1233                 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1234         mEditFilterAction.item.addSelectionListener(new SelectionAdapter() {
1235             @Override
1236             public void widgetSelected(SelectionEvent e) {
1237                 mLogPanel.editFilter();
1238             }
1239         });
1240
1241         mDeleteFilterAction = new ToolItemAction(toolBar, SWT.PUSH);
1242         mDeleteFilterAction.item.setToolTipText("Delete Filter");
1243         mDeleteFilterAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay,
1244                 "delete.png", //$NON-NLS-1$
1245                 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1246         mDeleteFilterAction.item.addSelectionListener(new SelectionAdapter() {
1247             @Override
1248             public void widgetSelected(SelectionEvent e) {
1249                 mLogPanel.deleteFilter();
1250             }
1251         });
1252
1253
1254         new ToolItem(toolBar, SWT.SEPARATOR);
1255
1256         LogLevel[] levels = LogLevel.values();
1257         mLogLevelActions = new ToolItemAction[mLogLevelIcons.length];
1258         for (int i = 0 ; i < mLogLevelActions.length; i++) {
1259             String name = levels[i].getStringValue();
1260             final ToolItemAction newAction = new ToolItemAction(toolBar, SWT.CHECK);
1261             mLogLevelActions[i] = newAction;
1262             //newAction.item.setText(name);
1263             newAction.item.addSelectionListener(new SelectionAdapter() {
1264                 @Override
1265                 public void widgetSelected(SelectionEvent e) {
1266                     // disable the other actions and record current index
1267                     for (int i = 0 ; i < mLogLevelActions.length; i++) {
1268                         ToolItemAction a = mLogLevelActions[i];
1269                         if (a == newAction) {
1270                             a.setChecked(true);
1271
1272                             // set the log level
1273                             mLogPanel.setCurrentFilterLogLevel(i+2);
1274                         } else {
1275                             a.setChecked(false);
1276                         }
1277                     }
1278                 }
1279             });
1280
1281             newAction.item.setToolTipText(name);
1282             newAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay,
1283                     mLogLevelIcons[i],
1284                     DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1285         }
1286
1287         new ToolItem(toolBar, SWT.SEPARATOR);
1288
1289         mClearAction = new ToolItemAction(toolBar, SWT.PUSH);
1290         mClearAction.item.setToolTipText("Clear Log");
1291
1292         mClearAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay,
1293                 "clear.png", //$NON-NLS-1$
1294                 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1295         mClearAction.item.addSelectionListener(new SelectionAdapter() {
1296             @Override
1297             public void widgetSelected(SelectionEvent e) {
1298                 mLogPanel.clear();
1299             }
1300         });
1301
1302         new ToolItem(toolBar, SWT.SEPARATOR);
1303
1304         mExportAction = new ToolItemAction(toolBar, SWT.PUSH);
1305         mExportAction.item.setToolTipText("Export Selection As Text...");
1306         mExportAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay,
1307                 "save.png", //$NON-NLS-1$
1308                 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1309         mExportAction.item.addSelectionListener(new SelectionAdapter() {
1310             @Override
1311             public void widgetSelected(SelectionEvent e) {
1312                 mLogPanel.save();
1313             }
1314         });
1315
1316
1317         toolBar.pack();
1318
1319         // now create the log view
1320         mLogPanel = new LogPanel(colors, new FilterStorage(), LogPanel.FILTER_MANUAL);
1321
1322         mLogPanel.setActions(mDeleteFilterAction, mEditFilterAction, mLogLevelActions);
1323
1324         String colMode = prefs.getString(PrefsDialog.LOGCAT_COLUMN_MODE);
1325         if (PrefsDialog.LOGCAT_COLUMN_MODE_AUTO.equals(colMode)) {
1326             mLogPanel.setColumnMode(LogPanel.COLUMN_MODE_AUTO);
1327         }
1328
1329         String fontStr = PrefsDialog.getStore().getString(PrefsDialog.LOGCAT_FONT);
1330         if (fontStr != null) {
1331             try {
1332                 FontData fdat = new FontData(fontStr);
1333                 mLogPanel.setFont(new Font(display, fdat));
1334             } catch (IllegalArgumentException e) {
1335                 // Looks like fontStr isn't a valid font representation.
1336                 // We do nothing in this case, the logcat view will use the default font.
1337             } catch (SWTError e2) {
1338                 // Looks like the Font() constructor failed.
1339                 // We do nothing in this case, the logcat view will use the default font.
1340             }
1341         }
1342
1343         mLogPanel.createPanel(comp);
1344
1345         // and start the logcat
1346         mLogPanel.startLogCat(mCurrentDevice);
1347     }
1348
1349     /*
1350      * Create the contents of the left panel: a table of VMs.
1351      */
1352     private void createLeftPanel(final Composite comp) {
1353         comp.setLayout(new GridLayout(1, false));
1354         ToolBar toolBar = new ToolBar(comp, SWT.HORIZONTAL | SWT.RIGHT | SWT.WRAP);
1355         toolBar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1356         createDevicePanelToolBar(toolBar);
1357
1358         Composite c = new Composite(comp, SWT.NONE);
1359         c.setLayoutData(new GridData(GridData.FILL_BOTH));
1360
1361         mDevicePanel = new DevicePanel(true /* showPorts */);
1362         mDevicePanel.createPanel(c);
1363
1364         // add ourselves to the device panel selection listener
1365         mDevicePanel.addSelectionListener(this);
1366     }
1367
1368     /*
1369      * Create the contents of the right panel: tabs with VM information.
1370      */
1371     private void createRightPanel(final Composite comp) {
1372         TabItem item;
1373         TabFolder tabFolder;
1374
1375         comp.setLayout(new FillLayout());
1376
1377         tabFolder = new TabFolder(comp, SWT.NONE);
1378
1379         for (int i = 0; i < mPanels.length; i++) {
1380             if (mPanels[i] != null) {
1381                 item = new TabItem(tabFolder, SWT.NONE);
1382                 item.setText(mPanelNames[i]);
1383                 item.setToolTipText(mPanelTips[i]);
1384                 item.setControl(mPanels[i].createPanel(tabFolder));
1385             }
1386         }
1387
1388         // add the emulator control panel to the folders.
1389         item = new TabItem(tabFolder, SWT.NONE);
1390         item.setText("Emulator Control");
1391         item.setToolTipText("Emulator Control Panel");
1392         mEmulatorPanel = new EmulatorControlPanel();
1393         item.setControl(mEmulatorPanel.createPanel(tabFolder));
1394
1395         // add the event log panel to the folders.
1396         item = new TabItem(tabFolder, SWT.NONE);
1397         item.setText("Event Log");
1398         item.setToolTipText("Event Log");
1399
1400         // create the composite that will hold the toolbar and the event log panel.
1401         Composite eventLogTopComposite = new Composite(tabFolder, SWT.NONE);
1402         item.setControl(eventLogTopComposite);
1403         eventLogTopComposite.setLayout(new GridLayout(1, false));
1404
1405         // create the toolbar and the actions
1406         ToolBar toolbar = new ToolBar(eventLogTopComposite, SWT.HORIZONTAL);
1407         toolbar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1408
1409         ToolItemAction optionsAction = new ToolItemAction(toolbar, SWT.PUSH);
1410         optionsAction.item.setToolTipText("Opens the options panel");
1411         optionsAction.item.setImage(mDdmUiLibLoader.loadImage(comp.getDisplay(),
1412                 "edit.png", //$NON-NLS-1$
1413                 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1414
1415         ToolItemAction clearAction = new ToolItemAction(toolbar, SWT.PUSH);
1416         clearAction.item.setToolTipText("Clears the event log");
1417         clearAction.item.setImage(mDdmUiLibLoader.loadImage(comp.getDisplay(),
1418                 "clear.png", //$NON-NLS-1$
1419                 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1420
1421         new ToolItem(toolbar, SWT.SEPARATOR);
1422
1423         ToolItemAction saveAction = new ToolItemAction(toolbar, SWT.PUSH);
1424         saveAction.item.setToolTipText("Saves the event log");
1425         saveAction.item.setImage(mDdmUiLibLoader.loadImage(comp.getDisplay(),
1426                 "save.png", //$NON-NLS-1$
1427                 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1428
1429         ToolItemAction loadAction = new ToolItemAction(toolbar, SWT.PUSH);
1430         loadAction.item.setToolTipText("Loads an event log");
1431         loadAction.item.setImage(mDdmUiLibLoader.loadImage(comp.getDisplay(),
1432                 "load.png", //$NON-NLS-1$
1433                 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1434
1435         ToolItemAction importBugAction = new ToolItemAction(toolbar, SWT.PUSH);
1436         importBugAction.item.setToolTipText("Imports a bug report");
1437         importBugAction.item.setImage(mDdmUiLibLoader.loadImage(comp.getDisplay(),
1438                 "importBug.png", //$NON-NLS-1$
1439                 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1440
1441         // create the event log panel
1442         mEventLogPanel = new EventLogPanel();
1443
1444         // set the external actions
1445         mEventLogPanel.setActions(optionsAction, clearAction, saveAction, loadAction,
1446                 importBugAction);
1447
1448         // create the panel
1449         mEventLogPanel.createPanel(eventLogTopComposite);
1450     }
1451
1452     private void createFileExplorer() {
1453         if (mExplorer == null) {
1454             mExplorerShell = new Shell(mDisplay);
1455
1456             // create the ui
1457             mExplorerShell.setLayout(new GridLayout(1, false));
1458
1459             // toolbar + action
1460             ToolBar toolBar = new ToolBar(mExplorerShell, SWT.HORIZONTAL);
1461             toolBar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1462
1463             ToolItemAction pullAction = new ToolItemAction(toolBar, SWT.PUSH);
1464             pullAction.item.setToolTipText("Pull File from Device");
1465             Image image = mDdmUiLibLoader.loadImage("pull.png", mDisplay); //$NON-NLS-1$
1466             if (image != null) {
1467                 pullAction.item.setImage(image);
1468             } else {
1469                 // this is for debugging purpose when the icon is missing
1470                 pullAction.item.setText("Pull"); //$NON-NLS-1$
1471             }
1472
1473             ToolItemAction pushAction = new ToolItemAction(toolBar, SWT.PUSH);
1474             pushAction.item.setToolTipText("Push file onto Device");
1475             image = mDdmUiLibLoader.loadImage("push.png", mDisplay); //$NON-NLS-1$
1476             if (image != null) {
1477                 pushAction.item.setImage(image);
1478             } else {
1479                 // this is for debugging purpose when the icon is missing
1480                 pushAction.item.setText("Push"); //$NON-NLS-1$
1481             }
1482
1483             ToolItemAction deleteAction = new ToolItemAction(toolBar, SWT.PUSH);
1484             deleteAction.item.setToolTipText("Delete");
1485             image = mDdmUiLibLoader.loadImage("delete.png", mDisplay); //$NON-NLS-1$
1486             if (image != null) {
1487                 deleteAction.item.setImage(image);
1488             } else {
1489                 // this is for debugging purpose when the icon is missing
1490                 deleteAction.item.setText("Delete"); //$NON-NLS-1$
1491             }
1492
1493             // device explorer
1494             mExplorer = new DeviceExplorer();
1495             mExplorer.setActions(pushAction, pullAction, deleteAction);
1496
1497             pullAction.item.addSelectionListener(new SelectionAdapter() {
1498                 @Override
1499                 public void widgetSelected(SelectionEvent e) {
1500                     mExplorer.pullSelection();
1501                 }
1502             });
1503             pullAction.setEnabled(false);
1504
1505             pushAction.item.addSelectionListener(new SelectionAdapter() {
1506                 @Override
1507                 public void widgetSelected(SelectionEvent e) {
1508                     mExplorer.pushIntoSelection();
1509                 }
1510             });
1511             pushAction.setEnabled(false);
1512
1513             deleteAction.item.addSelectionListener(new SelectionAdapter() {
1514                 @Override
1515                 public void widgetSelected(SelectionEvent e) {
1516                     mExplorer.deleteSelection();
1517                 }
1518             });
1519             deleteAction.setEnabled(false);
1520
1521             Composite parent = new Composite(mExplorerShell, SWT.NONE);
1522             parent.setLayoutData(new GridData(GridData.FILL_BOTH));
1523
1524             mExplorer.createPanel(parent);
1525             mExplorer.switchDevice(mCurrentDevice);
1526
1527             mExplorerShell.addShellListener(new ShellListener() {
1528                 public void shellActivated(ShellEvent e) {
1529                     // pass
1530                 }
1531
1532                 public void shellClosed(ShellEvent e) {
1533                     mExplorer = null;
1534                     mExplorerShell = null;
1535                 }
1536
1537                 public void shellDeactivated(ShellEvent e) {
1538                     // pass
1539                 }
1540
1541                 public void shellDeiconified(ShellEvent e) {
1542                     // pass
1543                 }
1544
1545                 public void shellIconified(ShellEvent e) {
1546                     // pass
1547                 }
1548             });
1549
1550             mExplorerShell.pack();
1551             setExplorerSizeAndPosition(mExplorerShell);
1552             mExplorerShell.open();
1553         } else {
1554             if (mExplorerShell != null) {
1555                 mExplorerShell.forceActive();
1556             }
1557         }
1558     }
1559
1560     /**
1561      * Set the status line. TODO: make this a stack, so we can safely have
1562      * multiple things trying to set it all at once. Also specify an expiration?
1563      */
1564     public void setStatusLine(final String str) {
1565         try {
1566             mDisplay.asyncExec(new Runnable() {
1567                 public void run() {
1568                     doSetStatusLine(str);
1569                 }
1570             });
1571         } catch (SWTException swte) {
1572             if (!mDisplay.isDisposed())
1573                 throw swte;
1574         }
1575     }
1576
1577     private void doSetStatusLine(String str) {
1578         if (mStatusLine.isDisposed())
1579             return;
1580
1581         if (!mStatusLine.getText().equals(str)) {
1582             mStatusLine.setText(str);
1583
1584             // try { Thread.sleep(100); }
1585             // catch (InterruptedException ie) {}
1586         }
1587     }
1588
1589     public void displayError(final String msg) {
1590         try {
1591             mDisplay.syncExec(new Runnable() {
1592                 public void run() {
1593                     MessageDialog.openError(mDisplay.getActiveShell(), "Error",
1594                             msg);
1595                 }
1596             });
1597         } catch (SWTException swte) {
1598             if (!mDisplay.isDisposed())
1599                 throw swte;
1600         }
1601     }
1602
1603     private void enableButtons() {
1604         if (mCurrentClient != null) {
1605             mTBShowThreadUpdates.setSelection(mCurrentClient.isThreadUpdateEnabled());
1606             mTBShowThreadUpdates.setEnabled(true);
1607             mTBShowHeapUpdates.setSelection(mCurrentClient.isHeapUpdateEnabled());
1608             mTBShowHeapUpdates.setEnabled(true);
1609             mTBHalt.setEnabled(true);
1610             mTBCauseGc.setEnabled(true);
1611
1612             ClientData data = mCurrentClient.getClientData();
1613
1614             if (data.hasFeature(ClientData.FEATURE_HPROF)) {
1615                 mTBDumpHprof.setEnabled(data.hasPendingHprofDump() == false);
1616                 mTBDumpHprof.setToolTipText("Dump HPROF file");
1617             } else {
1618                 mTBDumpHprof.setEnabled(false);
1619                 mTBDumpHprof.setToolTipText("Dump HPROF file (not supported by this VM)");
1620             }
1621
1622             if (data.hasFeature(ClientData.FEATURE_PROFILING)) {
1623                 mTBProfiling.setEnabled(true);
1624                 if (data.getMethodProfilingStatus() == MethodProfilingStatus.ON) {
1625                     mTBProfiling.setToolTipText("Stop Method Profiling");
1626                     mTBProfiling.setImage(mTracingStopImage);
1627                 } else {
1628                     mTBProfiling.setToolTipText("Start Method Profiling");
1629                     mTBProfiling.setImage(mTracingStartImage);
1630                 }
1631             } else {
1632                 mTBProfiling.setEnabled(false);
1633                 mTBProfiling.setImage(mTracingStartImage);
1634                 mTBProfiling.setToolTipText("Start Method Profiling (not supported by this VM)");
1635             }
1636         } else {
1637             // list is empty, disable these
1638             mTBShowThreadUpdates.setSelection(false);
1639             mTBShowThreadUpdates.setEnabled(false);
1640             mTBShowHeapUpdates.setSelection(false);
1641             mTBShowHeapUpdates.setEnabled(false);
1642             mTBHalt.setEnabled(false);
1643             mTBCauseGc.setEnabled(false);
1644
1645             mTBDumpHprof.setEnabled(false);
1646             mTBDumpHprof.setToolTipText("Dump HPROF file");
1647
1648             mTBProfiling.setEnabled(false);
1649             mTBProfiling.setImage(mTracingStartImage);
1650             mTBProfiling.setToolTipText("Start Method Profiling");
1651         }
1652     }
1653
1654     /**
1655      * Sent when a new {@link IDevice} and {@link Client} are selected.
1656      * @param selectedDevice the selected device. If null, no devices are selected.
1657      * @param selectedClient The selected client. If null, no clients are selected.
1658      *
1659      * @see IUiSelectionListener
1660      */
1661     public void selectionChanged(IDevice selectedDevice, Client selectedClient) {
1662         if (mCurrentDevice != selectedDevice) {
1663             mCurrentDevice = selectedDevice;
1664             for (TablePanel panel : mPanels) {
1665                 if (panel != null) {
1666                     panel.deviceSelected(mCurrentDevice);
1667                 }
1668             }
1669
1670             mEmulatorPanel.deviceSelected(mCurrentDevice);
1671             mLogPanel.deviceSelected(mCurrentDevice);
1672             if (mEventLogPanel != null) {
1673                 mEventLogPanel.deviceSelected(mCurrentDevice);
1674             }
1675
1676             if (mExplorer != null) {
1677                 mExplorer.switchDevice(mCurrentDevice);
1678             }
1679         }
1680
1681         if (mCurrentClient != selectedClient) {
1682             AndroidDebugBridge.getBridge().setSelectedClient(selectedClient);
1683             mCurrentClient = selectedClient;
1684             for (TablePanel panel : mPanels) {
1685                 if (panel != null) {
1686                     panel.clientSelected(mCurrentClient);
1687                 }
1688             }
1689
1690             enableButtons();
1691         }
1692     }
1693
1694     public void clientChanged(Client client, int changeMask) {
1695         if ((changeMask & Client.CHANGE_METHOD_PROFILING_STATUS) ==
1696                 Client.CHANGE_METHOD_PROFILING_STATUS) {
1697             if (mCurrentClient == client) {
1698                 mDisplay.asyncExec(new Runnable() {
1699                     public void run() {
1700                         // force refresh of the button enabled state.
1701                         enableButtons();
1702                     }
1703                 });
1704             }
1705         }
1706     }
1707 }