OSDN Git Service

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