2 * Copyright (C) 2007 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.ide.eclipse.ddms.views;
19 import com.android.ddmlib.Log.LogLevel;
20 import com.android.ddmuilib.ImageLoader;
21 import com.android.ddmuilib.logcat.LogColors;
22 import com.android.ddmuilib.logcat.LogFilter;
23 import com.android.ddmuilib.logcat.LogPanel;
24 import com.android.ddmuilib.logcat.LogPanel.ILogFilterStorageManager;
25 import com.android.ide.eclipse.ddms.CommonAction;
26 import com.android.ide.eclipse.ddms.DdmsPlugin;
27 import com.android.ide.eclipse.ddms.preferences.PreferenceInitializer;
29 import org.eclipse.core.resources.IFile;
30 import org.eclipse.core.resources.IMarker;
31 import org.eclipse.core.runtime.CoreException;
32 import org.eclipse.core.runtime.NullProgressMonitor;
33 import org.eclipse.core.runtime.Status;
34 import org.eclipse.jdt.core.search.IJavaSearchConstants;
35 import org.eclipse.jdt.core.search.SearchEngine;
36 import org.eclipse.jdt.core.search.SearchMatch;
37 import org.eclipse.jdt.core.search.SearchParticipant;
38 import org.eclipse.jdt.core.search.SearchPattern;
39 import org.eclipse.jdt.core.search.SearchRequestor;
40 import org.eclipse.jface.action.Action;
41 import org.eclipse.jface.action.IAction;
42 import org.eclipse.jface.action.IMenuManager;
43 import org.eclipse.jface.action.IToolBarManager;
44 import org.eclipse.jface.action.Separator;
45 import org.eclipse.swt.dnd.Clipboard;
46 import org.eclipse.swt.graphics.Color;
47 import org.eclipse.swt.graphics.Font;
48 import org.eclipse.swt.graphics.FontData;
49 import org.eclipse.swt.widgets.Composite;
50 import org.eclipse.swt.widgets.Display;
51 import org.eclipse.ui.IActionBars;
52 import org.eclipse.ui.IPerspectiveRegistry;
53 import org.eclipse.ui.IWorkbenchPage;
54 import org.eclipse.ui.IWorkbenchWindow;
55 import org.eclipse.ui.actions.ActionFactory;
56 import org.eclipse.ui.ide.IDE;
57 import org.eclipse.ui.internal.WorkbenchPlugin;
58 import org.eclipse.ui.internal.registry.PerspectiveDescriptor;
60 import java.util.ArrayList;
61 import java.util.HashMap;
62 import java.util.regex.Matcher;
63 import java.util.regex.Pattern;
66 * The log cat view displays log output from the current device selection.
69 public final class LogCatView extends SelectionDependentViewPart {
71 public static final String ID =
72 "com.android.ide.eclipse.ddms.views.LogCatView"; // $NON-NLS-1$
74 private static final String PREFS_COL_TIME =
75 DdmsPlugin.PLUGIN_ID + ".logcat.time"; // $NON-NLS-1$
76 private static final String PREFS_COL_LEVEL =
77 DdmsPlugin.PLUGIN_ID + ".logcat.level"; // $NON-NLS-1$
78 private static final String PREFS_COL_PID =
79 DdmsPlugin.PLUGIN_ID + ".logcat.pid"; // $NON-NLS-1$
80 private static final String PREFS_COL_TAG =
81 DdmsPlugin.PLUGIN_ID + ".logcat.tag"; // $NON-NLS-1$
82 private static final String PREFS_COL_MESSAGE =
83 DdmsPlugin.PLUGIN_ID + ".logcat.message"; // $NON-NLS-1$
85 private static final String PREFS_FILTERS =
86 DdmsPlugin.PLUGIN_ID + ".logcat.filters"; // $NON-NLS-1$
88 private static LogCatView sThis;
89 private LogPanel mLogPanel;
91 private CommonAction mCreateFilterAction;
92 private CommonAction mDeleteFilterAction;
93 private CommonAction mEditFilterAction;
94 private CommonAction mExportAction;
95 private CommonAction gotoLineAction;
97 private CommonAction[] mLogLevelActions;
98 private String[] mLogLevelIcons = {
99 "v.png", //$NON-NLS-1S
100 "d.png", //$NON-NLS-1S
101 "i.png", //$NON-NLS-1S
102 "w.png", //$NON-NLS-1S
103 "e.png", //$NON-NLS-1S
106 private Action mClearAction;
108 private Clipboard mClipboard;
111 * An implementation of {@link ILogFilterStorageManager} to bridge to the eclipse preference
112 * store, and saves the log filters.
114 private final class FilterStorage implements ILogFilterStorageManager {
116 public LogFilter[] getFilterFromStore() {
117 String filterPrefs = DdmsPlugin.getDefault().getPreferenceStore().getString(
120 // split in a string per filter
121 String[] filters = filterPrefs.split("\\|"); // $NON-NLS-1$
123 ArrayList<LogFilter> list =
124 new ArrayList<LogFilter>(filters.length);
126 for (String f : filters) {
127 if (f.length() > 0) {
128 LogFilter logFilter = new LogFilter();
129 if (logFilter.loadFromString(f)) {
135 return list.toArray(new LogFilter[list.size()]);
138 public void saveFilters(LogFilter[] filters) {
139 StringBuilder sb = new StringBuilder();
140 for (LogFilter f : filters) {
141 String filterString = f.toString();
142 sb.append(filterString);
146 DdmsPlugin.getDefault().getPreferenceStore().setValue(PREFS_FILTERS, sb.toString());
149 public boolean requiresDefaultFilter() {
154 public LogCatView() {
156 LogPanel.PREFS_TIME = PREFS_COL_TIME;
157 LogPanel.PREFS_LEVEL = PREFS_COL_LEVEL;
158 LogPanel.PREFS_PID = PREFS_COL_PID;
159 LogPanel.PREFS_TAG = PREFS_COL_TAG;
160 LogPanel.PREFS_MESSAGE = PREFS_COL_MESSAGE;
164 * Returns the singleton instance.
166 public static LogCatView getInstance() {
171 * Sets the display font.
172 * @param font The font.
174 public static void setFont(Font font) {
175 if (sThis != null && sThis.mLogPanel != null) {
176 sThis.mLogPanel.setFont(font);
181 public void createPartControl(Composite parent) {
182 Display d = parent.getDisplay();
183 LogColors colors = new LogColors();
185 ImageLoader loader = ImageLoader.getDdmUiLibLoader();
187 colors.infoColor = new Color(d, 0, 127, 0);
188 colors.debugColor = new Color(d, 0, 0, 127);
189 colors.errorColor = new Color(d, 255, 0, 0);
190 colors.warningColor = new Color(d, 255, 127, 0);
191 colors.verboseColor = new Color(d, 0, 0, 0);
193 mCreateFilterAction = new CommonAction("Create Filter") {
196 mLogPanel.addFilter();
199 mCreateFilterAction.setToolTipText("Create Filter");
200 mCreateFilterAction.setImageDescriptor(loader.loadDescriptor("add.png"));
202 mEditFilterAction = new CommonAction("Edit Filter") {
205 mLogPanel.editFilter();
208 mEditFilterAction.setToolTipText("Edit Filter");
209 mEditFilterAction.setImageDescriptor(loader.loadDescriptor("edit.png")); // $NON-NLS-1$
211 mDeleteFilterAction = new CommonAction("Delete Filter") {
214 mLogPanel.deleteFilter();
217 mDeleteFilterAction.setToolTipText("Delete Filter");
218 mDeleteFilterAction.setImageDescriptor(loader.loadDescriptor("delete.png")); // $NON-NLS-1$
220 mExportAction = new CommonAction("Export Selection As Text...") {
226 mExportAction.setToolTipText("Export Selection As Text...");
227 mExportAction.setImageDescriptor(loader.loadDescriptor("save.png")); // $NON-NLS-1$
229 gotoLineAction = new CommonAction("Go to Problem") {
236 LogLevel[] levels = LogLevel.values();
237 mLogLevelActions = new CommonAction[mLogLevelIcons.length];
238 for (int i = 0 ; i < mLogLevelActions.length; i++) {
239 String name = levels[i].getStringValue();
240 mLogLevelActions[i] = new CommonAction(name, IAction.AS_CHECK_BOX) {
243 // disable the other actions and record current index
244 for (int i = 0 ; i < mLogLevelActions.length; i++) {
245 Action a = mLogLevelActions[i];
250 mLogPanel.setCurrentFilterLogLevel(i+2);
258 mLogLevelActions[i].setToolTipText(name);
259 mLogLevelActions[i].setImageDescriptor(loader.loadDescriptor(mLogLevelIcons[i]));
262 mClearAction = new Action("Clear Log") {
268 mClearAction.setImageDescriptor(loader.loadDescriptor("clear.png")); // $NON-NLS-1$
271 // now create the log view
272 mLogPanel = new LogPanel(colors, new FilterStorage(), LogPanel.FILTER_MANUAL);
273 mLogPanel.setActions(mDeleteFilterAction, mEditFilterAction, mLogLevelActions);
276 String fontStr = DdmsPlugin.getDefault().getPreferenceStore().getString(
277 PreferenceInitializer.ATTR_LOGCAT_FONT);
278 if (fontStr != null) {
279 FontData data = new FontData(fontStr);
281 if (fontStr != null) {
282 mLogPanel.setFont(new Font(parent.getDisplay(), data));
286 mLogPanel.createPanel(parent);
287 setSelectionDependentPanel(mLogPanel);
289 // place the actions.
292 // setup the copy action
293 mClipboard = new Clipboard(d);
294 IActionBars actionBars = getViewSite().getActionBars();
295 actionBars.setGlobalActionHandler(ActionFactory.COPY.getId(), new Action("Copy") {
298 mLogPanel.copy(mClipboard);
302 // setup the select all action
303 actionBars.setGlobalActionHandler(ActionFactory.SELECT_ALL.getId(),
304 new Action("Select All") {
307 mLogPanel.selectAll();
313 public void dispose() {
314 mLogPanel.stopLogCat(true);
315 mClipboard.dispose();
319 public void setFocus() {
320 mLogPanel.setFocus();
324 * Place the actions in the ui.
326 private void placeActions() {
327 IActionBars actionBars = getViewSite().getActionBars();
330 IMenuManager menuManager = actionBars.getMenuManager();
331 menuManager.add(mCreateFilterAction);
332 menuManager.add(mEditFilterAction);
333 menuManager.add(mDeleteFilterAction);
334 menuManager.add(new Separator());
335 menuManager.add(mClearAction);
336 menuManager.add(new Separator());
337 menuManager.add(mExportAction);
338 menuManager.add(gotoLineAction);
340 // and then in the toolbar
341 IToolBarManager toolBarManager = actionBars.getToolBarManager();
342 for (CommonAction a : mLogLevelActions) {
343 toolBarManager.add(a);
345 toolBarManager.add(new Separator());
346 toolBarManager.add(mCreateFilterAction);
347 toolBarManager.add(mEditFilterAction);
348 toolBarManager.add(mDeleteFilterAction);
349 toolBarManager.add(new Separator());
350 toolBarManager.add(mClearAction);
353 IMarker createMarkerFromSearchMatch(IFile file, SearchMatch match) {
354 HashMap<String, Object> map = new HashMap<String, Object>();
355 map.put(IMarker.CHAR_START, new Integer(match.getOffset()));
356 map.put(IMarker.CHAR_END, new Integer(match.getOffset()
357 + match.getLength()));
358 IMarker marker = null;
360 marker = file.createMarker(IMarker.TEXT);
361 marker.setAttributes(map);
362 } catch (CoreException e) {
363 Status s = new Status(Status.ERROR, DdmsPlugin.PLUGIN_ID, e.getMessage(), e);
364 DdmsPlugin.getDefault().getLog().log(s);
369 void openFile(IFile file, IMarker marker) {
371 IWorkbenchPage page = getViewSite().getWorkbenchWindow()
374 IDE.openEditor(page, marker);
377 } catch (CoreException e) {
378 Status s = new Status(Status.ERROR, DdmsPlugin.PLUGIN_ID, e.getMessage(), e);
379 DdmsPlugin.getDefault().getLog().log(s);
383 void switchPerspective() {
385 IWorkbenchWindow window = getViewSite().getWorkbenchWindow()
386 .getWorkbench().getActiveWorkbenchWindow();
387 String rtPerspectiveId = "org.eclipse.jdt.ui.JavaPerspective";
388 IPerspectiveRegistry reg = WorkbenchPlugin.getDefault()
389 .getPerspectiveRegistry();
390 PerspectiveDescriptor rtPerspectiveDesc = (PerspectiveDescriptor) reg
391 .findPerspectiveWithId(rtPerspectiveId);
392 if (window != null) {
393 IWorkbenchPage page = window.getActivePage();
394 page.setPerspective(rtPerspectiveDesc);
398 void goToErrorLine() {
400 String msg = mLogPanel.getSelectedErrorLineMessage();
402 String error_line_matcher_string = "\\s*at\\ (.*)\\((.*\\.java)\\:(\\d+)\\)";
403 Matcher error_line_matcher = Pattern.compile(
404 error_line_matcher_string).matcher(msg);
406 if (error_line_matcher.find()) {
407 String class_name = error_line_matcher.group(1);
409 // TODO: Search currently only matches the class declaration (using
410 // IJavaSearchConstants.DECLARATIONS). We may want to jump to the
411 // "reference" of the class instead (IJavaSearchConstants.REFERENCES)
412 // using the filename and line number to disambiguate the search results.
413 // String filename = error_line_matcher.group(2);
414 // int line_number = Integer.parseInt(error_line_matcher.group(3));
416 SearchEngine se = new SearchEngine();
417 se.search(SearchPattern.createPattern(class_name,
418 IJavaSearchConstants.METHOD,
419 IJavaSearchConstants.DECLARATIONS,
420 SearchPattern.R_EXACT_MATCH
421 | SearchPattern.R_CASE_SENSITIVE),
422 new SearchParticipant[] { SearchEngine
423 .getDefaultSearchParticipant() },
424 SearchEngine.createWorkspaceScope(),
425 new SearchRequestor() {
426 boolean found_first_match = false;
429 public void acceptSearchMatch(
431 throws CoreException {
433 if (match.getResource() instanceof IFile
434 && !found_first_match) {
435 found_first_match = true;
437 IFile matched_file = (IFile) match
439 IMarker marker = createMarkerFromSearchMatch(
440 matched_file, match);
442 // There should only be one exact match,
443 // so we go immediately to that one.
445 openFile(matched_file, marker);
448 }, new NullProgressMonitor());
452 } catch (Exception e) {
453 Status s = new Status(Status.ERROR, DdmsPlugin.PLUGIN_ID, e.getMessage(), e);
454 DdmsPlugin.getDefault().getLog().log(s);