OSDN Git Service

merge from tools_r9
[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' | (Main.isMac() ? SWT.COMMAND : 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         final MenuItem actionResetAdb = new MenuItem(actionMenu, SWT.NONE);
773         actionResetAdb.setText("&Reset adb");
774         actionResetAdb.addSelectionListener(new SelectionAdapter() {
775             @Override
776             public void widgetSelected(SelectionEvent e) {
777                 AndroidDebugBridge bridge = AndroidDebugBridge.getBridge();
778                 if (bridge != null) {
779                     bridge.restart();
780                 }
781             }
782         });
783
784         // configure Action items based on current state
785         actionMenu.addMenuListener(new MenuAdapter() {
786             @Override
787             public void menuShown(MenuEvent e) {
788                 actionHaltItem.setEnabled(mTBHalt.getEnabled() && mCurrentClient != null);
789                 actionCauseGcItem.setEnabled(mTBCauseGc.getEnabled() && mCurrentClient != null);
790                 actionResetAdb.setEnabled(true);
791             }
792         });
793
794         // create Device menu items
795         final MenuItem screenShotItem = new MenuItem(deviceMenu, SWT.NONE);
796
797         // The \tCtrl-S "keybinding text" here isn't right for the Mac - but
798         // it's stripped out and replaced by the proper keyboard accelerator
799         // text (e.g. the unicode symbol for the command key + S) anyway
800         // so it's fine to leave it there for the other platforms.
801         screenShotItem.setText("&Screen capture...\tCtrl-S");
802         screenShotItem.setAccelerator('S' | (Main.isMac() ? SWT.COMMAND : SWT.CONTROL));
803         screenShotItem.addSelectionListener(new SelectionAdapter() {
804             @Override
805             public void widgetSelected(SelectionEvent e) {
806                 if (mCurrentDevice != null) {
807                     ScreenShotDialog dlg = new ScreenShotDialog(shell);
808                     dlg.open(mCurrentDevice);
809                 }
810             }
811         });
812
813         new MenuItem(deviceMenu, SWT.SEPARATOR);
814
815         final MenuItem explorerItem = new MenuItem(deviceMenu, SWT.NONE);
816         explorerItem.setText("File Explorer...");
817         explorerItem.addSelectionListener(new SelectionAdapter() {
818             @Override
819             public void widgetSelected(SelectionEvent e) {
820                 createFileExplorer();
821             }
822         });
823
824         new MenuItem(deviceMenu, SWT.SEPARATOR);
825
826         final MenuItem processItem = new MenuItem(deviceMenu, SWT.NONE);
827         processItem.setText("Show &process status...");
828         processItem.addSelectionListener(new SelectionAdapter() {
829             @Override
830             public void widgetSelected(SelectionEvent e) {
831                 DeviceCommandDialog dlg;
832                 dlg = new DeviceCommandDialog("ps -x", "ps-x.txt", shell);
833                 dlg.open(mCurrentDevice);
834             }
835         });
836
837         final MenuItem deviceStateItem = new MenuItem(deviceMenu, SWT.NONE);
838         deviceStateItem.setText("Dump &device state...");
839         deviceStateItem.addSelectionListener(new SelectionAdapter() {
840             @Override
841             public void widgetSelected(SelectionEvent e) {
842                 DeviceCommandDialog dlg;
843                 dlg = new DeviceCommandDialog("/system/bin/dumpstate /proc/self/fd/0",
844                         "device-state.txt", shell);
845                 dlg.open(mCurrentDevice);
846             }
847         });
848
849         final MenuItem appStateItem = new MenuItem(deviceMenu, SWT.NONE);
850         appStateItem.setText("Dump &app state...");
851         appStateItem.setEnabled(false);
852         appStateItem.addSelectionListener(new SelectionAdapter() {
853             @Override
854             public void widgetSelected(SelectionEvent e) {
855                 DeviceCommandDialog dlg;
856                 dlg = new DeviceCommandDialog("dumpsys", "app-state.txt", shell);
857                 dlg.open(mCurrentDevice);
858             }
859         });
860
861         final MenuItem radioStateItem = new MenuItem(deviceMenu, SWT.NONE);
862         radioStateItem.setText("Dump &radio state...");
863         radioStateItem.addSelectionListener(new SelectionAdapter() {
864             @Override
865             public void widgetSelected(SelectionEvent e) {
866                 DeviceCommandDialog dlg;
867                 dlg = new DeviceCommandDialog(
868                         "cat /data/logs/radio.4 /data/logs/radio.3"
869                         + " /data/logs/radio.2 /data/logs/radio.1"
870                         + " /data/logs/radio",
871                         "radio-state.txt", shell);
872                 dlg.open(mCurrentDevice);
873             }
874         });
875
876         final MenuItem logCatItem = new MenuItem(deviceMenu, SWT.NONE);
877         logCatItem.setText("Run &logcat...");
878         logCatItem.addSelectionListener(new SelectionAdapter() {
879             @Override
880             public void widgetSelected(SelectionEvent e) {
881                 DeviceCommandDialog dlg;
882                 dlg = new DeviceCommandDialog("logcat '*:d jdwp:w'", "log.txt",
883                         shell);
884                 dlg.open(mCurrentDevice);
885             }
886         });
887
888         // configure Action items based on current state
889         deviceMenu.addMenuListener(new MenuAdapter() {
890             @Override
891             public void menuShown(MenuEvent e) {
892                 boolean deviceEnabled = mCurrentDevice != null;
893                 screenShotItem.setEnabled(deviceEnabled);
894                 explorerItem.setEnabled(deviceEnabled);
895                 processItem.setEnabled(deviceEnabled);
896                 deviceStateItem.setEnabled(deviceEnabled);
897                 appStateItem.setEnabled(deviceEnabled);
898                 radioStateItem.setEnabled(deviceEnabled);
899                 logCatItem.setEnabled(deviceEnabled);
900             }
901         });
902
903         // create Help menu items
904         item = new MenuItem(helpMenu, SWT.NONE);
905         item.setText("&Contents...");
906         item.addSelectionListener(new SelectionAdapter() {
907             @Override
908             public void widgetSelected(SelectionEvent e) {
909                 int style = SWT.APPLICATION_MODAL | SWT.OK;
910                 MessageBox msgBox = new MessageBox(shell, style);
911                 msgBox.setText("Help!");
912                 msgBox.setMessage("Help wanted.");
913                 msgBox.open();
914             }
915         });
916
917         new MenuItem(helpMenu, SWT.SEPARATOR);
918
919         item = new MenuItem(helpMenu, SWT.NONE);
920         item.setText("&About...");
921         item.addSelectionListener(new SelectionAdapter() {
922             @Override
923             public void widgetSelected(SelectionEvent e) {
924                 AboutDialog dlg = new AboutDialog(shell);
925                 dlg.open();
926             }
927         });
928
929         // tell the shell to use this menu
930         shell.setMenuBar(menuBar);
931     }
932
933     /*
934      * Create the widgets in the main application window. The basic layout is a
935      * two-panel sash, with a scrolling list of VMs on the left and detailed
936      * output for a single VM on the right.
937      */
938     private void createWidgets(final Shell shell) {
939         Color darkGray = shell.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY);
940
941         /*
942          * Create three areas: tool bar, split panels, status line
943          */
944         shell.setLayout(new GridLayout(1, false));
945
946         // 1. panel area
947         final Composite panelArea = new Composite(shell, SWT.BORDER);
948
949         // make the panel area absorb all space
950         panelArea.setLayoutData(new GridData(GridData.FILL_BOTH));
951
952         // 2. status line.
953         mStatusLine = new Label(shell, SWT.NONE);
954
955         // make status line extend all the way across
956         mStatusLine.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
957
958         mStatusLine.setText("Initializing...");
959
960         /*
961          * Configure the split-panel area.
962          */
963         final PreferenceStore prefs = PrefsDialog.getStore();
964
965         Composite topPanel = new Composite(panelArea, SWT.NONE);
966         final Sash sash = new Sash(panelArea, SWT.HORIZONTAL);
967         sash.setBackground(darkGray);
968         Composite bottomPanel = new Composite(panelArea, SWT.NONE);
969
970         panelArea.setLayout(new FormLayout());
971
972         createTopPanel(topPanel, darkGray);
973         createBottomPanel(bottomPanel);
974
975         // form layout data
976         FormData data = new FormData();
977         data.top = new FormAttachment(0, 0);
978         data.bottom = new FormAttachment(sash, 0);
979         data.left = new FormAttachment(0, 0);
980         data.right = new FormAttachment(100, 0);
981         topPanel.setLayoutData(data);
982
983         final FormData sashData = new FormData();
984         if (prefs != null && prefs.contains(PREFERENCE_LOGSASH)) {
985             sashData.top = new FormAttachment(0, prefs.getInt(
986                     PREFERENCE_LOGSASH));
987         } else {
988             sashData.top = new FormAttachment(50,0); // 50% across
989         }
990         sashData.left = new FormAttachment(0, 0);
991         sashData.right = new FormAttachment(100, 0);
992         sash.setLayoutData(sashData);
993
994         data = new FormData();
995         data.top = new FormAttachment(sash, 0);
996         data.bottom = new FormAttachment(100, 0);
997         data.left = new FormAttachment(0, 0);
998         data.right = new FormAttachment(100, 0);
999         bottomPanel.setLayoutData(data);
1000
1001         // allow resizes, but cap at minPanelWidth
1002         sash.addListener(SWT.Selection, new Listener() {
1003             public void handleEvent(Event e) {
1004                 Rectangle sashRect = sash.getBounds();
1005                 Rectangle panelRect = panelArea.getClientArea();
1006                 int bottom = panelRect.height - sashRect.height - 100;
1007                 e.y = Math.max(Math.min(e.y, bottom), 100);
1008                 if (e.y != sashRect.y) {
1009                     sashData.top = new FormAttachment(0, e.y);
1010                     prefs.setValue(PREFERENCE_LOGSASH, e.y);
1011                     panelArea.layout();
1012                 }
1013             }
1014         });
1015
1016         // add a global focus listener for all the tables
1017         mTableListener = new TableFocusListener();
1018
1019         // now set up the listener in the various panels
1020         mLogPanel.setTableFocusListener(mTableListener);
1021         mEventLogPanel.setTableFocusListener(mTableListener);
1022         for (TablePanel p : mPanels) {
1023             if (p != null) {
1024                 p.setTableFocusListener(mTableListener);
1025             }
1026         }
1027
1028         mStatusLine.setText("");
1029     }
1030
1031     /*
1032      * Populate the tool bar.
1033      */
1034     private void createDevicePanelToolBar(ToolBar toolBar) {
1035         Display display = toolBar.getDisplay();
1036
1037         // add "show heap updates" button
1038         mTBShowHeapUpdates = new ToolItem(toolBar, SWT.CHECK);
1039         mTBShowHeapUpdates.setImage(mDdmUiLibLoader.loadImage(display,
1040                 DevicePanel.ICON_HEAP, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1041         mTBShowHeapUpdates.setToolTipText("Show heap updates");
1042         mTBShowHeapUpdates.setEnabled(false);
1043         mTBShowHeapUpdates.addSelectionListener(new SelectionAdapter() {
1044             @Override
1045             public void widgetSelected(SelectionEvent e) {
1046                 if (mCurrentClient != null) {
1047                     // boolean status = ((ToolItem)e.item).getSelection();
1048                     // invert previous state
1049                     boolean enable = !mCurrentClient.isHeapUpdateEnabled();
1050                     mCurrentClient.setHeapUpdateEnabled(enable);
1051                 } else {
1052                     e.doit = false; // this has no effect?
1053                 }
1054             }
1055         });
1056
1057         // add "dump HPROF" button
1058         mTBDumpHprof = new ToolItem(toolBar, SWT.PUSH);
1059         mTBDumpHprof.setToolTipText("Dump HPROF file");
1060         mTBDumpHprof.setEnabled(false);
1061         mTBDumpHprof.setImage(mDdmUiLibLoader.loadImage(display,
1062                 DevicePanel.ICON_HPROF, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1063         mTBDumpHprof.addSelectionListener(new SelectionAdapter() {
1064             @Override
1065             public void widgetSelected(SelectionEvent e) {
1066                 mDevicePanel.dumpHprof();
1067
1068                 // this will make sure the dump hprof button is disabled for the current selection
1069                 // as the client is already dumping an hprof file
1070                 enableButtons();
1071             }
1072         });
1073
1074         // add "cause GC" button
1075         mTBCauseGc = new ToolItem(toolBar, SWT.PUSH);
1076         mTBCauseGc.setToolTipText("Cause an immediate GC");
1077         mTBCauseGc.setEnabled(false);
1078         mTBCauseGc.setImage(mDdmUiLibLoader.loadImage(display,
1079                 DevicePanel.ICON_GC, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1080         mTBCauseGc.addSelectionListener(new SelectionAdapter() {
1081             @Override
1082             public void widgetSelected(SelectionEvent e) {
1083                 mDevicePanel.forceGcOnSelectedClient();
1084             }
1085         });
1086
1087         new ToolItem(toolBar, SWT.SEPARATOR);
1088
1089         // add "show thread updates" button
1090         mTBShowThreadUpdates = new ToolItem(toolBar, SWT.CHECK);
1091         mTBShowThreadUpdates.setImage(mDdmUiLibLoader.loadImage(display,
1092                 DevicePanel.ICON_THREAD, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1093         mTBShowThreadUpdates.setToolTipText("Show thread updates");
1094         mTBShowThreadUpdates.setEnabled(false);
1095         mTBShowThreadUpdates.addSelectionListener(new SelectionAdapter() {
1096             @Override
1097             public void widgetSelected(SelectionEvent e) {
1098                 if (mCurrentClient != null) {
1099                     // boolean status = ((ToolItem)e.item).getSelection();
1100                     // invert previous state
1101                     boolean enable = !mCurrentClient.isThreadUpdateEnabled();
1102
1103                     mCurrentClient.setThreadUpdateEnabled(enable);
1104                 } else {
1105                     e.doit = false; // this has no effect?
1106                 }
1107             }
1108         });
1109
1110         // add a start/stop method tracing
1111         mTracingStartImage = mDdmUiLibLoader.loadImage(display,
1112                 DevicePanel.ICON_TRACING_START,
1113                 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null);
1114         mTracingStopImage = mDdmUiLibLoader.loadImage(display,
1115                 DevicePanel.ICON_TRACING_STOP,
1116                 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null);
1117         mTBProfiling = new ToolItem(toolBar, SWT.PUSH);
1118         mTBProfiling.setToolTipText("Start Method Profiling");
1119         mTBProfiling.setEnabled(false);
1120         mTBProfiling.setImage(mTracingStartImage);
1121         mTBProfiling.addSelectionListener(new SelectionAdapter() {
1122             @Override
1123             public void widgetSelected(SelectionEvent e) {
1124                 mDevicePanel.toggleMethodProfiling();
1125             }
1126         });
1127
1128         new ToolItem(toolBar, SWT.SEPARATOR);
1129
1130         // add "kill VM" button; need to make this visually distinct from
1131         // the status update buttons
1132         mTBHalt = new ToolItem(toolBar, SWT.PUSH);
1133         mTBHalt.setToolTipText("Halt the target VM");
1134         mTBHalt.setEnabled(false);
1135         mTBHalt.setImage(mDdmUiLibLoader.loadImage(display,
1136                 DevicePanel.ICON_HALT, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1137         mTBHalt.addSelectionListener(new SelectionAdapter() {
1138             @Override
1139             public void widgetSelected(SelectionEvent e) {
1140                 mDevicePanel.killSelectedClient();
1141             }
1142         });
1143
1144        toolBar.pack();
1145     }
1146
1147     private void createTopPanel(final Composite comp, Color darkGray) {
1148         final PreferenceStore prefs = PrefsDialog.getStore();
1149
1150         comp.setLayout(new FormLayout());
1151
1152         Composite leftPanel = new Composite(comp, SWT.NONE);
1153         final Sash sash = new Sash(comp, SWT.VERTICAL);
1154         sash.setBackground(darkGray);
1155         Composite rightPanel = new Composite(comp, SWT.NONE);
1156
1157         createLeftPanel(leftPanel);
1158         createRightPanel(rightPanel);
1159
1160         FormData data = new FormData();
1161         data.top = new FormAttachment(0, 0);
1162         data.bottom = new FormAttachment(100, 0);
1163         data.left = new FormAttachment(0, 0);
1164         data.right = new FormAttachment(sash, 0);
1165         leftPanel.setLayoutData(data);
1166
1167         final FormData sashData = new FormData();
1168         sashData.top = new FormAttachment(0, 0);
1169         sashData.bottom = new FormAttachment(100, 0);
1170         if (prefs != null && prefs.contains(PREFERENCE_SASH)) {
1171             sashData.left = new FormAttachment(0, prefs.getInt(
1172                     PREFERENCE_SASH));
1173         } else {
1174             // position the sash 380 from the right instead of x% (done by using
1175             // FormAttachment(x, 0)) in order to keep the sash at the same
1176             // position
1177             // from the left when the window is resized.
1178             // 380px is just enough to display the left table with no horizontal
1179             // scrollbar with the default font.
1180             sashData.left = new FormAttachment(0, 380);
1181         }
1182         sash.setLayoutData(sashData);
1183
1184         data = new FormData();
1185         data.top = new FormAttachment(0, 0);
1186         data.bottom = new FormAttachment(100, 0);
1187         data.left = new FormAttachment(sash, 0);
1188         data.right = new FormAttachment(100, 0);
1189         rightPanel.setLayoutData(data);
1190
1191         final int minPanelWidth = 60;
1192
1193         // allow resizes, but cap at minPanelWidth
1194         sash.addListener(SWT.Selection, new Listener() {
1195             public void handleEvent(Event e) {
1196                 Rectangle sashRect = sash.getBounds();
1197                 Rectangle panelRect = comp.getClientArea();
1198                 int right = panelRect.width - sashRect.width - minPanelWidth;
1199                 e.x = Math.max(Math.min(e.x, right), minPanelWidth);
1200                 if (e.x != sashRect.x) {
1201                     sashData.left = new FormAttachment(0, e.x);
1202                     prefs.setValue(PREFERENCE_SASH, e.x);
1203                     comp.layout();
1204                 }
1205             }
1206         });
1207     }
1208
1209     private void createBottomPanel(final Composite comp) {
1210         final PreferenceStore prefs = PrefsDialog.getStore();
1211
1212         // create clipboard
1213         Display display = comp.getDisplay();
1214         mClipboard = new Clipboard(display);
1215
1216         LogColors colors = new LogColors();
1217
1218         colors.infoColor = new Color(display, 0, 127, 0);
1219         colors.debugColor = new Color(display, 0, 0, 127);
1220         colors.errorColor = new Color(display, 255, 0, 0);
1221         colors.warningColor = new Color(display, 255, 127, 0);
1222         colors.verboseColor = new Color(display, 0, 0, 0);
1223
1224         // set the preferences names
1225         LogPanel.PREFS_TIME = PREFS_COL_TIME;
1226         LogPanel.PREFS_LEVEL = PREFS_COL_LEVEL;
1227         LogPanel.PREFS_PID = PREFS_COL_PID;
1228         LogPanel.PREFS_TAG = PREFS_COL_TAG;
1229         LogPanel.PREFS_MESSAGE = PREFS_COL_MESSAGE;
1230
1231         comp.setLayout(new GridLayout(1, false));
1232
1233         ToolBar toolBar = new ToolBar(comp, SWT.HORIZONTAL);
1234
1235         mCreateFilterAction = new ToolItemAction(toolBar, SWT.PUSH);
1236         mCreateFilterAction.item.setToolTipText("Create Filter");
1237         mCreateFilterAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay,
1238                 "add.png", //$NON-NLS-1$
1239                 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1240         mCreateFilterAction.item.addSelectionListener(new SelectionAdapter() {
1241             @Override
1242             public void widgetSelected(SelectionEvent e) {
1243                 mLogPanel.addFilter();
1244             }
1245         });
1246
1247         mEditFilterAction = new ToolItemAction(toolBar, SWT.PUSH);
1248         mEditFilterAction.item.setToolTipText("Edit Filter");
1249         mEditFilterAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay,
1250                 "edit.png", //$NON-NLS-1$
1251                 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1252         mEditFilterAction.item.addSelectionListener(new SelectionAdapter() {
1253             @Override
1254             public void widgetSelected(SelectionEvent e) {
1255                 mLogPanel.editFilter();
1256             }
1257         });
1258
1259         mDeleteFilterAction = new ToolItemAction(toolBar, SWT.PUSH);
1260         mDeleteFilterAction.item.setToolTipText("Delete Filter");
1261         mDeleteFilterAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay,
1262                 "delete.png", //$NON-NLS-1$
1263                 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1264         mDeleteFilterAction.item.addSelectionListener(new SelectionAdapter() {
1265             @Override
1266             public void widgetSelected(SelectionEvent e) {
1267                 mLogPanel.deleteFilter();
1268             }
1269         });
1270
1271
1272         new ToolItem(toolBar, SWT.SEPARATOR);
1273
1274         LogLevel[] levels = LogLevel.values();
1275         mLogLevelActions = new ToolItemAction[mLogLevelIcons.length];
1276         for (int i = 0 ; i < mLogLevelActions.length; i++) {
1277             String name = levels[i].getStringValue();
1278             final ToolItemAction newAction = new ToolItemAction(toolBar, SWT.CHECK);
1279             mLogLevelActions[i] = newAction;
1280             //newAction.item.setText(name);
1281             newAction.item.addSelectionListener(new SelectionAdapter() {
1282                 @Override
1283                 public void widgetSelected(SelectionEvent e) {
1284                     // disable the other actions and record current index
1285                     for (int i = 0 ; i < mLogLevelActions.length; i++) {
1286                         ToolItemAction a = mLogLevelActions[i];
1287                         if (a == newAction) {
1288                             a.setChecked(true);
1289
1290                             // set the log level
1291                             mLogPanel.setCurrentFilterLogLevel(i+2);
1292                         } else {
1293                             a.setChecked(false);
1294                         }
1295                     }
1296                 }
1297             });
1298
1299             newAction.item.setToolTipText(name);
1300             newAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay,
1301                     mLogLevelIcons[i],
1302                     DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1303         }
1304
1305         new ToolItem(toolBar, SWT.SEPARATOR);
1306
1307         mClearAction = new ToolItemAction(toolBar, SWT.PUSH);
1308         mClearAction.item.setToolTipText("Clear Log");
1309
1310         mClearAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay,
1311                 "clear.png", //$NON-NLS-1$
1312                 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1313         mClearAction.item.addSelectionListener(new SelectionAdapter() {
1314             @Override
1315             public void widgetSelected(SelectionEvent e) {
1316                 mLogPanel.clear();
1317             }
1318         });
1319
1320         new ToolItem(toolBar, SWT.SEPARATOR);
1321
1322         mExportAction = new ToolItemAction(toolBar, SWT.PUSH);
1323         mExportAction.item.setToolTipText("Export Selection As Text...");
1324         mExportAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay,
1325                 "save.png", //$NON-NLS-1$
1326                 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1327         mExportAction.item.addSelectionListener(new SelectionAdapter() {
1328             @Override
1329             public void widgetSelected(SelectionEvent e) {
1330                 mLogPanel.save();
1331             }
1332         });
1333
1334
1335         toolBar.pack();
1336
1337         // now create the log view
1338         mLogPanel = new LogPanel(colors, new FilterStorage(), LogPanel.FILTER_MANUAL);
1339
1340         mLogPanel.setActions(mDeleteFilterAction, mEditFilterAction, mLogLevelActions);
1341
1342         String colMode = prefs.getString(PrefsDialog.LOGCAT_COLUMN_MODE);
1343         if (PrefsDialog.LOGCAT_COLUMN_MODE_AUTO.equals(colMode)) {
1344             mLogPanel.setColumnMode(LogPanel.COLUMN_MODE_AUTO);
1345         }
1346
1347         String fontStr = PrefsDialog.getStore().getString(PrefsDialog.LOGCAT_FONT);
1348         if (fontStr != null) {
1349             try {
1350                 FontData fdat = new FontData(fontStr);
1351                 mLogPanel.setFont(new Font(display, fdat));
1352             } catch (IllegalArgumentException e) {
1353                 // Looks like fontStr isn't a valid font representation.
1354                 // We do nothing in this case, the logcat view will use the default font.
1355             } catch (SWTError e2) {
1356                 // Looks like the Font() constructor failed.
1357                 // We do nothing in this case, the logcat view will use the default font.
1358             }
1359         }
1360
1361         mLogPanel.createPanel(comp);
1362
1363         // and start the logcat
1364         mLogPanel.startLogCat(mCurrentDevice);
1365     }
1366
1367     /*
1368      * Create the contents of the left panel: a table of VMs.
1369      */
1370     private void createLeftPanel(final Composite comp) {
1371         comp.setLayout(new GridLayout(1, false));
1372         ToolBar toolBar = new ToolBar(comp, SWT.HORIZONTAL | SWT.RIGHT | SWT.WRAP);
1373         toolBar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1374         createDevicePanelToolBar(toolBar);
1375
1376         Composite c = new Composite(comp, SWT.NONE);
1377         c.setLayoutData(new GridData(GridData.FILL_BOTH));
1378
1379         mDevicePanel = new DevicePanel(true /* showPorts */);
1380         mDevicePanel.createPanel(c);
1381
1382         // add ourselves to the device panel selection listener
1383         mDevicePanel.addSelectionListener(this);
1384     }
1385
1386     /*
1387      * Create the contents of the right panel: tabs with VM information.
1388      */
1389     private void createRightPanel(final Composite comp) {
1390         TabItem item;
1391         TabFolder tabFolder;
1392
1393         comp.setLayout(new FillLayout());
1394
1395         tabFolder = new TabFolder(comp, SWT.NONE);
1396
1397         for (int i = 0; i < mPanels.length; i++) {
1398             if (mPanels[i] != null) {
1399                 item = new TabItem(tabFolder, SWT.NONE);
1400                 item.setText(mPanelNames[i]);
1401                 item.setToolTipText(mPanelTips[i]);
1402                 item.setControl(mPanels[i].createPanel(tabFolder));
1403             }
1404         }
1405
1406         // add the emulator control panel to the folders.
1407         item = new TabItem(tabFolder, SWT.NONE);
1408         item.setText("Emulator Control");
1409         item.setToolTipText("Emulator Control Panel");
1410         mEmulatorPanel = new EmulatorControlPanel();
1411         item.setControl(mEmulatorPanel.createPanel(tabFolder));
1412
1413         // add the event log panel to the folders.
1414         item = new TabItem(tabFolder, SWT.NONE);
1415         item.setText("Event Log");
1416         item.setToolTipText("Event Log");
1417
1418         // create the composite that will hold the toolbar and the event log panel.
1419         Composite eventLogTopComposite = new Composite(tabFolder, SWT.NONE);
1420         item.setControl(eventLogTopComposite);
1421         eventLogTopComposite.setLayout(new GridLayout(1, false));
1422
1423         // create the toolbar and the actions
1424         ToolBar toolbar = new ToolBar(eventLogTopComposite, SWT.HORIZONTAL);
1425         toolbar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1426
1427         ToolItemAction optionsAction = new ToolItemAction(toolbar, SWT.PUSH);
1428         optionsAction.item.setToolTipText("Opens the options panel");
1429         optionsAction.item.setImage(mDdmUiLibLoader.loadImage(comp.getDisplay(),
1430                 "edit.png", //$NON-NLS-1$
1431                 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1432
1433         ToolItemAction clearAction = new ToolItemAction(toolbar, SWT.PUSH);
1434         clearAction.item.setToolTipText("Clears the event log");
1435         clearAction.item.setImage(mDdmUiLibLoader.loadImage(comp.getDisplay(),
1436                 "clear.png", //$NON-NLS-1$
1437                 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1438
1439         new ToolItem(toolbar, SWT.SEPARATOR);
1440
1441         ToolItemAction saveAction = new ToolItemAction(toolbar, SWT.PUSH);
1442         saveAction.item.setToolTipText("Saves the event log");
1443         saveAction.item.setImage(mDdmUiLibLoader.loadImage(comp.getDisplay(),
1444                 "save.png", //$NON-NLS-1$
1445                 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1446
1447         ToolItemAction loadAction = new ToolItemAction(toolbar, SWT.PUSH);
1448         loadAction.item.setToolTipText("Loads an event log");
1449         loadAction.item.setImage(mDdmUiLibLoader.loadImage(comp.getDisplay(),
1450                 "load.png", //$NON-NLS-1$
1451                 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1452
1453         ToolItemAction importBugAction = new ToolItemAction(toolbar, SWT.PUSH);
1454         importBugAction.item.setToolTipText("Imports a bug report");
1455         importBugAction.item.setImage(mDdmUiLibLoader.loadImage(comp.getDisplay(),
1456                 "importBug.png", //$NON-NLS-1$
1457                 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1458
1459         // create the event log panel
1460         mEventLogPanel = new EventLogPanel();
1461
1462         // set the external actions
1463         mEventLogPanel.setActions(optionsAction, clearAction, saveAction, loadAction,
1464                 importBugAction);
1465
1466         // create the panel
1467         mEventLogPanel.createPanel(eventLogTopComposite);
1468     }
1469
1470     private void createFileExplorer() {
1471         if (mExplorer == null) {
1472             mExplorerShell = new Shell(mDisplay);
1473
1474             // create the ui
1475             mExplorerShell.setLayout(new GridLayout(1, false));
1476
1477             // toolbar + action
1478             ToolBar toolBar = new ToolBar(mExplorerShell, SWT.HORIZONTAL);
1479             toolBar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1480
1481             ToolItemAction pullAction = new ToolItemAction(toolBar, SWT.PUSH);
1482             pullAction.item.setToolTipText("Pull File from Device");
1483             Image image = mDdmUiLibLoader.loadImage("pull.png", mDisplay); //$NON-NLS-1$
1484             if (image != null) {
1485                 pullAction.item.setImage(image);
1486             } else {
1487                 // this is for debugging purpose when the icon is missing
1488                 pullAction.item.setText("Pull"); //$NON-NLS-1$
1489             }
1490
1491             ToolItemAction pushAction = new ToolItemAction(toolBar, SWT.PUSH);
1492             pushAction.item.setToolTipText("Push file onto Device");
1493             image = mDdmUiLibLoader.loadImage("push.png", mDisplay); //$NON-NLS-1$
1494             if (image != null) {
1495                 pushAction.item.setImage(image);
1496             } else {
1497                 // this is for debugging purpose when the icon is missing
1498                 pushAction.item.setText("Push"); //$NON-NLS-1$
1499             }
1500
1501             ToolItemAction deleteAction = new ToolItemAction(toolBar, SWT.PUSH);
1502             deleteAction.item.setToolTipText("Delete");
1503             image = mDdmUiLibLoader.loadImage("delete.png", mDisplay); //$NON-NLS-1$
1504             if (image != null) {
1505                 deleteAction.item.setImage(image);
1506             } else {
1507                 // this is for debugging purpose when the icon is missing
1508                 deleteAction.item.setText("Delete"); //$NON-NLS-1$
1509             }
1510
1511             // device explorer
1512             mExplorer = new DeviceExplorer();
1513             mExplorer.setActions(pushAction, pullAction, deleteAction);
1514
1515             pullAction.item.addSelectionListener(new SelectionAdapter() {
1516                 @Override
1517                 public void widgetSelected(SelectionEvent e) {
1518                     mExplorer.pullSelection();
1519                 }
1520             });
1521             pullAction.setEnabled(false);
1522
1523             pushAction.item.addSelectionListener(new SelectionAdapter() {
1524                 @Override
1525                 public void widgetSelected(SelectionEvent e) {
1526                     mExplorer.pushIntoSelection();
1527                 }
1528             });
1529             pushAction.setEnabled(false);
1530
1531             deleteAction.item.addSelectionListener(new SelectionAdapter() {
1532                 @Override
1533                 public void widgetSelected(SelectionEvent e) {
1534                     mExplorer.deleteSelection();
1535                 }
1536             });
1537             deleteAction.setEnabled(false);
1538
1539             Composite parent = new Composite(mExplorerShell, SWT.NONE);
1540             parent.setLayoutData(new GridData(GridData.FILL_BOTH));
1541
1542             mExplorer.createPanel(parent);
1543             mExplorer.switchDevice(mCurrentDevice);
1544
1545             mExplorerShell.addShellListener(new ShellListener() {
1546                 public void shellActivated(ShellEvent e) {
1547                     // pass
1548                 }
1549
1550                 public void shellClosed(ShellEvent e) {
1551                     mExplorer = null;
1552                     mExplorerShell = null;
1553                 }
1554
1555                 public void shellDeactivated(ShellEvent e) {
1556                     // pass
1557                 }
1558
1559                 public void shellDeiconified(ShellEvent e) {
1560                     // pass
1561                 }
1562
1563                 public void shellIconified(ShellEvent e) {
1564                     // pass
1565                 }
1566             });
1567
1568             mExplorerShell.pack();
1569             setExplorerSizeAndPosition(mExplorerShell);
1570             mExplorerShell.open();
1571         } else {
1572             if (mExplorerShell != null) {
1573                 mExplorerShell.forceActive();
1574             }
1575         }
1576     }
1577
1578     /**
1579      * Set the status line. TODO: make this a stack, so we can safely have
1580      * multiple things trying to set it all at once. Also specify an expiration?
1581      */
1582     public void setStatusLine(final String str) {
1583         try {
1584             mDisplay.asyncExec(new Runnable() {
1585                 public void run() {
1586                     doSetStatusLine(str);
1587                 }
1588             });
1589         } catch (SWTException swte) {
1590             if (!mDisplay.isDisposed())
1591                 throw swte;
1592         }
1593     }
1594
1595     private void doSetStatusLine(String str) {
1596         if (mStatusLine.isDisposed())
1597             return;
1598
1599         if (!mStatusLine.getText().equals(str)) {
1600             mStatusLine.setText(str);
1601
1602             // try { Thread.sleep(100); }
1603             // catch (InterruptedException ie) {}
1604         }
1605     }
1606
1607     public void displayError(final String msg) {
1608         try {
1609             mDisplay.syncExec(new Runnable() {
1610                 public void run() {
1611                     MessageDialog.openError(mDisplay.getActiveShell(), "Error",
1612                             msg);
1613                 }
1614             });
1615         } catch (SWTException swte) {
1616             if (!mDisplay.isDisposed())
1617                 throw swte;
1618         }
1619     }
1620
1621     private void enableButtons() {
1622         if (mCurrentClient != null) {
1623             mTBShowThreadUpdates.setSelection(mCurrentClient.isThreadUpdateEnabled());
1624             mTBShowThreadUpdates.setEnabled(true);
1625             mTBShowHeapUpdates.setSelection(mCurrentClient.isHeapUpdateEnabled());
1626             mTBShowHeapUpdates.setEnabled(true);
1627             mTBHalt.setEnabled(true);
1628             mTBCauseGc.setEnabled(true);
1629
1630             ClientData data = mCurrentClient.getClientData();
1631
1632             if (data.hasFeature(ClientData.FEATURE_HPROF)) {
1633                 mTBDumpHprof.setEnabled(data.hasPendingHprofDump() == false);
1634                 mTBDumpHprof.setToolTipText("Dump HPROF file");
1635             } else {
1636                 mTBDumpHprof.setEnabled(false);
1637                 mTBDumpHprof.setToolTipText("Dump HPROF file (not supported by this VM)");
1638             }
1639
1640             if (data.hasFeature(ClientData.FEATURE_PROFILING)) {
1641                 mTBProfiling.setEnabled(true);
1642                 if (data.getMethodProfilingStatus() == MethodProfilingStatus.ON) {
1643                     mTBProfiling.setToolTipText("Stop Method Profiling");
1644                     mTBProfiling.setImage(mTracingStopImage);
1645                 } else {
1646                     mTBProfiling.setToolTipText("Start Method Profiling");
1647                     mTBProfiling.setImage(mTracingStartImage);
1648                 }
1649             } else {
1650                 mTBProfiling.setEnabled(false);
1651                 mTBProfiling.setImage(mTracingStartImage);
1652                 mTBProfiling.setToolTipText("Start Method Profiling (not supported by this VM)");
1653             }
1654         } else {
1655             // list is empty, disable these
1656             mTBShowThreadUpdates.setSelection(false);
1657             mTBShowThreadUpdates.setEnabled(false);
1658             mTBShowHeapUpdates.setSelection(false);
1659             mTBShowHeapUpdates.setEnabled(false);
1660             mTBHalt.setEnabled(false);
1661             mTBCauseGc.setEnabled(false);
1662
1663             mTBDumpHprof.setEnabled(false);
1664             mTBDumpHprof.setToolTipText("Dump HPROF file");
1665
1666             mTBProfiling.setEnabled(false);
1667             mTBProfiling.setImage(mTracingStartImage);
1668             mTBProfiling.setToolTipText("Start Method Profiling");
1669         }
1670     }
1671
1672     /**
1673      * Sent when a new {@link IDevice} and {@link Client} are selected.
1674      * @param selectedDevice the selected device. If null, no devices are selected.
1675      * @param selectedClient The selected client. If null, no clients are selected.
1676      *
1677      * @see IUiSelectionListener
1678      */
1679     public void selectionChanged(IDevice selectedDevice, Client selectedClient) {
1680         if (mCurrentDevice != selectedDevice) {
1681             mCurrentDevice = selectedDevice;
1682             for (TablePanel panel : mPanels) {
1683                 if (panel != null) {
1684                     panel.deviceSelected(mCurrentDevice);
1685                 }
1686             }
1687
1688             mEmulatorPanel.deviceSelected(mCurrentDevice);
1689             mLogPanel.deviceSelected(mCurrentDevice);
1690             if (mEventLogPanel != null) {
1691                 mEventLogPanel.deviceSelected(mCurrentDevice);
1692             }
1693
1694             if (mExplorer != null) {
1695                 mExplorer.switchDevice(mCurrentDevice);
1696             }
1697         }
1698
1699         if (mCurrentClient != selectedClient) {
1700             AndroidDebugBridge.getBridge().setSelectedClient(selectedClient);
1701             mCurrentClient = selectedClient;
1702             for (TablePanel panel : mPanels) {
1703                 if (panel != null) {
1704                     panel.clientSelected(mCurrentClient);
1705                 }
1706             }
1707
1708             enableButtons();
1709         }
1710     }
1711
1712     public void clientChanged(Client client, int changeMask) {
1713         if ((changeMask & Client.CHANGE_METHOD_PROFILING_STATUS) ==
1714                 Client.CHANGE_METHOD_PROFILING_STATUS) {
1715             if (mCurrentClient == client) {
1716                 mDisplay.asyncExec(new Runnable() {
1717                     public void run() {
1718                         // force refresh of the button enabled state.
1719                         enableButtons();
1720                     }
1721                 });
1722             }
1723         }
1724     }
1725 }