• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.ddmuilib.logcat;
18 
19 import com.android.ddmlib.AdbCommandRejectedException;
20 import com.android.ddmlib.IDevice;
21 import com.android.ddmlib.Log;
22 import com.android.ddmlib.Log.LogLevel;
23 import com.android.ddmlib.MultiLineReceiver;
24 import com.android.ddmlib.ShellCommandUnresponsiveException;
25 import com.android.ddmlib.TimeoutException;
26 import com.android.ddmuilib.DdmUiPreferences;
27 import com.android.ddmuilib.ITableFocusListener;
28 import com.android.ddmuilib.ITableFocusListener.IFocusedTableActivator;
29 import com.android.ddmuilib.SelectionDependentPanel;
30 import com.android.ddmuilib.TableHelper;
31 import com.android.ddmuilib.actions.ICommonAction;
32 
33 import org.eclipse.jface.preference.IPreferenceStore;
34 import org.eclipse.swt.SWT;
35 import org.eclipse.swt.SWTException;
36 import org.eclipse.swt.dnd.Clipboard;
37 import org.eclipse.swt.dnd.TextTransfer;
38 import org.eclipse.swt.dnd.Transfer;
39 import org.eclipse.swt.events.ControlEvent;
40 import org.eclipse.swt.events.ControlListener;
41 import org.eclipse.swt.events.FocusEvent;
42 import org.eclipse.swt.events.FocusListener;
43 import org.eclipse.swt.events.ModifyEvent;
44 import org.eclipse.swt.events.ModifyListener;
45 import org.eclipse.swt.events.SelectionAdapter;
46 import org.eclipse.swt.events.SelectionEvent;
47 import org.eclipse.swt.graphics.Font;
48 import org.eclipse.swt.graphics.Rectangle;
49 import org.eclipse.swt.layout.FillLayout;
50 import org.eclipse.swt.layout.GridData;
51 import org.eclipse.swt.layout.GridLayout;
52 import org.eclipse.swt.widgets.Composite;
53 import org.eclipse.swt.widgets.Control;
54 import org.eclipse.swt.widgets.Display;
55 import org.eclipse.swt.widgets.FileDialog;
56 import org.eclipse.swt.widgets.Label;
57 import org.eclipse.swt.widgets.TabFolder;
58 import org.eclipse.swt.widgets.TabItem;
59 import org.eclipse.swt.widgets.Table;
60 import org.eclipse.swt.widgets.TableColumn;
61 import org.eclipse.swt.widgets.TableItem;
62 import org.eclipse.swt.widgets.Text;
63 
64 import java.io.FileWriter;
65 import java.io.IOException;
66 import java.util.ArrayList;
67 import java.util.Arrays;
68 import java.util.regex.Matcher;
69 import java.util.regex.Pattern;
70 
71 public class LogPanel extends SelectionDependentPanel {
72 
73     private static final int STRING_BUFFER_LENGTH = 10000;
74 
75     /** no filtering. Only one tab with everything. */
76     public static final int FILTER_NONE = 0;
77     /** manual mode for filter. all filters are manually created. */
78     public static final int FILTER_MANUAL = 1;
79     /** automatic mode for filter (pid mode).
80      * All filters are automatically created. */
81     public static final int FILTER_AUTO_PID = 2;
82     /** automatic mode for filter (tag mode).
83      * All filters are automatically created. */
84     public static final int FILTER_AUTO_TAG = 3;
85     /** Manual filtering mode + new filter for debug app, if needed */
86     public static final int FILTER_DEBUG = 4;
87 
88     public static final int COLUMN_MODE_MANUAL = 0;
89     public static final int COLUMN_MODE_AUTO = 1;
90 
91     public static String PREFS_TIME;
92     public static String PREFS_LEVEL;
93     public static String PREFS_PID;
94     public static String PREFS_TAG;
95     public static String PREFS_MESSAGE;
96 
97     /**
98      * This pattern is meant to parse the first line of a log message with the option
99      * 'logcat -v long'. The first line represents the date, tag, severity, etc.. while the
100      * following lines are the message (can be several line).<br>
101      * This first line looks something like<br>
102      * <code>"[ 00-00 00:00:00.000 &lt;pid&gt;:0x&lt;???&gt; &lt;severity&gt;/&lt;tag&gt;]"</code>
103      * <br>
104      * Note: severity is one of V, D, I, W, or EM<br>
105      * Note: the fraction of second value can have any number of digit.
106      * Note the tag should be trim as it may have spaces at the end.
107      */
108     private static Pattern sLogPattern = Pattern.compile(
109             "^\\[\\s(\\d\\d-\\d\\d\\s\\d\\d:\\d\\d:\\d\\d\\.\\d+)" + //$NON-NLS-1$
110             "\\s+(\\d*):(0x[0-9a-fA-F]+)\\s([VDIWE])/(.*)\\]$"); //$NON-NLS-1$
111 
112     /**
113      * Interface for Storage Filter manager. Implementation of this interface
114      * provide a custom way to archive an reload filters.
115      */
116     public interface ILogFilterStorageManager {
117 
getFilterFromStore()118         public LogFilter[] getFilterFromStore();
119 
saveFilters(LogFilter[] filters)120         public void saveFilters(LogFilter[] filters);
121 
requiresDefaultFilter()122         public boolean requiresDefaultFilter();
123     }
124 
125     private Composite mParent;
126     private IPreferenceStore mStore;
127 
128     /** top object in the view */
129     private TabFolder mFolders;
130 
131     private LogColors mColors;
132 
133     private ILogFilterStorageManager mFilterStorage;
134 
135     private LogCatOuputReceiver mCurrentLogCat;
136 
137     /**
138      * Circular buffer containing the logcat output. This is unfiltered.
139      * The valid content goes from <code>mBufferStart</code> to
140      * <code>mBufferEnd - 1</code>. Therefore its number of item is
141      * <code>mBufferEnd - mBufferStart</code>.
142      */
143     private LogMessage[] mBuffer = new LogMessage[STRING_BUFFER_LENGTH];
144 
145     /** Represents the oldest message in the buffer */
146     private int mBufferStart = -1;
147 
148     /**
149      * Represents the next usable item in the buffer to receive new message.
150      * This can be equal to mBufferStart, but when used mBufferStart will be
151      * incremented as well.
152      */
153     private int mBufferEnd = -1;
154 
155     /** Filter list */
156     private LogFilter[] mFilters;
157 
158     /** Default filter */
159     private LogFilter mDefaultFilter;
160 
161     /** Current filter being displayed */
162     private LogFilter mCurrentFilter;
163 
164     /** Filtering mode */
165     private int mFilterMode = FILTER_NONE;
166 
167     /** Device currently running logcat */
168     private IDevice mCurrentLoggedDevice = null;
169 
170     private ICommonAction mDeleteFilterAction;
171     private ICommonAction mEditFilterAction;
172 
173     private ICommonAction[] mLogLevelActions;
174 
175     /** message data, separated from content for multi line messages */
176     protected static class LogMessageInfo {
177         public LogLevel logLevel;
178         public int pid;
179         public String pidString;
180         public String tag;
181         public String time;
182     }
183 
184     /** pointer to the latest LogMessageInfo. this is used for multi line
185      * log message, to reuse the info regarding level, pid, etc...
186      */
187     private LogMessageInfo mLastMessageInfo = null;
188 
189     private boolean mPendingAsyncRefresh = false;
190 
191     private String mDefaultLogSave;
192 
193     private int mColumnMode = COLUMN_MODE_MANUAL;
194     private Font mDisplayFont;
195 
196     private ITableFocusListener mGlobalListener;
197 
198     private LogCatViewInterface mLogCatViewInterface = null;
199 
200     /** message data, separated from content for multi line messages */
201     protected static class LogMessage {
202         public LogMessageInfo data;
203         public String msg;
204 
205         @Override
toString()206         public String toString() {
207             return data.time + ": " //$NON-NLS-1$
208                 + data.logLevel + "/" //$NON-NLS-1$
209                 + data.tag + "(" //$NON-NLS-1$
210                 + data.pidString + "): " //$NON-NLS-1$
211                 + msg;
212         }
213     }
214 
215     /**
216      * objects able to receive the output of a remote shell command,
217      * specifically a logcat command in this case
218      */
219     private final class LogCatOuputReceiver extends MultiLineReceiver {
220 
221         public boolean isCancelled = false;
222 
LogCatOuputReceiver()223         public LogCatOuputReceiver() {
224             super();
225 
226             setTrimLine(false);
227         }
228 
229         @Override
processNewLines(String[] lines)230         public void processNewLines(String[] lines) {
231             if (isCancelled == false) {
232                 processLogLines(lines);
233             }
234         }
235 
236         @Override
isCancelled()237         public boolean isCancelled() {
238             return isCancelled;
239         }
240     }
241 
242     /**
243      * Parser class for the output of a "ps" shell command executed on a device.
244      * This class looks for a specific pid to find the process name from it.
245      * Once found, the name is used to update a filter and a tab object
246      *
247      */
248     private class PsOutputReceiver extends MultiLineReceiver {
249 
250         private LogFilter mFilter;
251 
252         private TabItem mTabItem;
253 
254         private int mPid;
255 
256         /** set to true when we've found the pid we're looking for */
257         private boolean mDone = false;
258 
PsOutputReceiver(int pid, LogFilter filter, TabItem tabItem)259         PsOutputReceiver(int pid, LogFilter filter, TabItem tabItem) {
260             mPid = pid;
261             mFilter = filter;
262             mTabItem = tabItem;
263         }
264 
265         @Override
isCancelled()266         public boolean isCancelled() {
267             return mDone;
268         }
269 
270         @Override
processNewLines(String[] lines)271         public void processNewLines(String[] lines) {
272             for (String line : lines) {
273                 if (line.startsWith("USER")) { //$NON-NLS-1$
274                     continue;
275                 }
276                 // get the pid.
277                 int index = line.indexOf(' ');
278                 if (index == -1) {
279                     continue;
280                 }
281                 // look for the next non blank char
282                 index++;
283                 while (line.charAt(index) == ' ') {
284                     index++;
285                 }
286 
287                 // this is the start of the pid.
288                 // look for the end.
289                 int index2 = line.indexOf(' ', index);
290 
291                 // get the line
292                 String pidStr = line.substring(index, index2);
293                 int pid = Integer.parseInt(pidStr);
294                 if (pid != mPid) {
295                     continue;
296                 } else {
297                     // get the process name
298                     index = line.lastIndexOf(' ');
299                     final String name = line.substring(index + 1);
300 
301                     mFilter.setName(name);
302 
303                     // update the tab
304                     Display d = mFolders.getDisplay();
305                     d.asyncExec(new Runnable() {
306                        @Override
307                     public void run() {
308                            mTabItem.setText(name);
309                        }
310                     });
311 
312                     // we're done with this ps.
313                     mDone = true;
314                     return;
315                 }
316             }
317         }
318 
319     }
320 
321     /**
322      * Interface implemented by the LogCatView in Eclipse for particular action on double-click.
323      */
324     public interface LogCatViewInterface {
onDoubleClick()325         public void onDoubleClick();
326     }
327 
328     /**
329      * Create the log view with some default parameters
330      * @param colors The display color object
331      * @param filterStorage the storage for user defined filters.
332      * @param mode The filtering mode
333      */
LogPanel(LogColors colors, ILogFilterStorageManager filterStorage, int mode)334     public LogPanel(LogColors colors,
335             ILogFilterStorageManager filterStorage, int mode) {
336         mColors = colors;
337         mFilterMode = mode;
338         mFilterStorage = filterStorage;
339         mStore = DdmUiPreferences.getStore();
340     }
341 
setActions(ICommonAction deleteAction, ICommonAction editAction, ICommonAction[] logLevelActions)342     public void setActions(ICommonAction deleteAction, ICommonAction editAction,
343             ICommonAction[] logLevelActions) {
344         mDeleteFilterAction = deleteAction;
345         mEditFilterAction = editAction;
346         mLogLevelActions = logLevelActions;
347     }
348 
349     /**
350      * Sets the column mode. Must be called before creatUI
351      * @param mode the column mode. Valid values are COLUMN_MOD_MANUAL and
352      *  COLUMN_MODE_AUTO
353      */
setColumnMode(int mode)354     public void setColumnMode(int mode) {
355         mColumnMode  = mode;
356     }
357 
358     /**
359      * Sets the display font.
360      * @param font The display font.
361      */
setFont(Font font)362     public void setFont(Font font) {
363         mDisplayFont = font;
364 
365         if (mFilters != null) {
366             for (LogFilter f : mFilters) {
367                 Table table = f.getTable();
368                 if (table != null) {
369                     table.setFont(font);
370                 }
371             }
372         }
373 
374         if (mDefaultFilter != null) {
375             Table table = mDefaultFilter.getTable();
376             if (table != null) {
377                 table.setFont(font);
378             }
379         }
380     }
381 
382     /**
383      * Sent when a new device is selected. The new device can be accessed
384      * with {@link #getCurrentDevice()}.
385      */
386     @Override
deviceSelected()387     public void deviceSelected() {
388         startLogCat(getCurrentDevice());
389     }
390 
391     /**
392      * Sent when a new client is selected. The new client can be accessed
393      * with {@link #getCurrentClient()}.
394      */
395     @Override
clientSelected()396     public void clientSelected() {
397         // pass
398     }
399 
400 
401     /**
402      * Creates a control capable of displaying some information.  This is
403      * called once, when the application is initializing, from the UI thread.
404      */
405     @Override
createControl(Composite parent)406     protected Control createControl(Composite parent) {
407         mParent = parent;
408 
409         Composite top = new Composite(parent, SWT.NONE);
410         top.setLayoutData(new GridData(GridData.FILL_BOTH));
411         top.setLayout(new GridLayout(1, false));
412 
413         // create the tab folder
414         mFolders = new TabFolder(top, SWT.NONE);
415         mFolders.setLayoutData(new GridData(GridData.FILL_BOTH));
416         mFolders.addSelectionListener(new SelectionAdapter() {
417             @Override
418             public void widgetSelected(SelectionEvent e) {
419                 if (mCurrentFilter != null) {
420                     mCurrentFilter.setSelectedState(false);
421                 }
422                 mCurrentFilter = getCurrentFilter();
423                 mCurrentFilter.setSelectedState(true);
424                 updateColumns(mCurrentFilter.getTable());
425                 if (mCurrentFilter.getTempFilterStatus()) {
426                     initFilter(mCurrentFilter);
427                 }
428                 selectionChanged(mCurrentFilter);
429             }
430         });
431 
432 
433         Composite bottom = new Composite(top, SWT.NONE);
434         bottom.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
435         bottom.setLayout(new GridLayout(3, false));
436 
437         Label label = new Label(bottom, SWT.NONE);
438         label.setText("Filter:");
439 
440         final Text filterText = new Text(bottom, SWT.SINGLE | SWT.BORDER);
441         filterText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
442         filterText.addModifyListener(new ModifyListener() {
443             @Override
444             public void modifyText(ModifyEvent e) {
445                 updateFilteringWith(filterText.getText());
446             }
447         });
448 
449         /*
450         Button addFilterBtn = new Button(bottom, SWT.NONE);
451         addFilterBtn.setImage(mImageLoader.loadImage("add.png", //$NON-NLS-1$
452                 addFilterBtn.getDisplay()));
453         */
454 
455         // get the filters
456         createFilters();
457 
458         // for each filter, create a tab.
459         int index = 0;
460 
461         if (mDefaultFilter != null) {
462             createTab(mDefaultFilter, index++, false);
463         }
464 
465         if (mFilters != null) {
466             for (LogFilter f : mFilters) {
467                 createTab(f, index++, false);
468             }
469         }
470 
471         return top;
472     }
473 
474     @Override
postCreation()475     protected void postCreation() {
476         // pass
477     }
478 
479     /**
480      * Sets the focus to the proper object.
481      */
482     @Override
setFocus()483     public void setFocus() {
484         mFolders.setFocus();
485     }
486 
487 
488     /**
489      * Starts a new logcat and set mCurrentLogCat as the current receiver.
490      * @param device the device to connect logcat to.
491      */
startLogCat(final IDevice device)492     public void startLogCat(final IDevice device) {
493         if (device == mCurrentLoggedDevice) {
494             return;
495         }
496 
497         // if we have a logcat already running
498         if (mCurrentLoggedDevice != null) {
499             stopLogCat(false);
500             mCurrentLoggedDevice = null;
501         }
502 
503         resetUI(false);
504 
505         if (device != null) {
506             // create a new output receiver
507             mCurrentLogCat = new LogCatOuputReceiver();
508 
509             // start the logcat in a different thread
510             new Thread("Logcat")  { //$NON-NLS-1$
511                 @Override
512                 public void run() {
513 
514                     while (device.isOnline() == false &&
515                             mCurrentLogCat != null &&
516                             mCurrentLogCat.isCancelled == false) {
517                         try {
518                             sleep(2000);
519                         } catch (InterruptedException e) {
520                             return;
521                         }
522                     }
523 
524                     if (mCurrentLogCat == null || mCurrentLogCat.isCancelled) {
525                         // logcat was stopped/cancelled before the device became ready.
526                         return;
527                     }
528 
529                     try {
530                         mCurrentLoggedDevice = device;
531                         device.executeShellCommand("logcat -v long", mCurrentLogCat, 0 /*timeout*/); //$NON-NLS-1$
532                     } catch (Exception e) {
533                         Log.e("Logcat", e);
534                     } finally {
535                         // at this point the command is terminated.
536                         mCurrentLogCat = null;
537                         mCurrentLoggedDevice = null;
538                     }
539                 }
540             }.start();
541         }
542     }
543 
544     /** Stop the current logcat */
stopLogCat(boolean inUiThread)545     public void stopLogCat(boolean inUiThread) {
546         if (mCurrentLogCat != null) {
547             mCurrentLogCat.isCancelled = true;
548 
549             // when the thread finishes, no one will reference that object
550             // and it'll be destroyed
551             mCurrentLogCat = null;
552 
553             // reset the content buffer
554             for (int i = 0 ; i < STRING_BUFFER_LENGTH; i++) {
555                 mBuffer[i] = null;
556             }
557 
558             // because it's a circular buffer, it's hard to know if
559             // the array is empty with both start/end at 0 or if it's full
560             // with both start/end at 0 as well. So to mean empty, we use -1
561             mBufferStart = -1;
562             mBufferEnd = -1;
563 
564             resetFilters();
565             resetUI(inUiThread);
566         }
567     }
568 
569     /**
570      * Adds a new Filter. This methods displays the UI to create the filter
571      * and set up its parameters.<br>
572      * <b>MUST</b> be called from the ui thread.
573      *
574      */
addFilter()575     public void addFilter() {
576         EditFilterDialog dlg = new EditFilterDialog(mFolders.getShell());
577         if (dlg.open()) {
578             synchronized (mBuffer) {
579                 // get the new filter in the array
580                 LogFilter filter = dlg.getFilter();
581                 addFilterToArray(filter);
582 
583                 int index = mFilters.length - 1;
584                 if (mDefaultFilter != null) {
585                     index++;
586                 }
587 
588                 if (false) {
589 
590                     for (LogFilter f : mFilters) {
591                         if (f.uiReady()) {
592                             f.dispose();
593                         }
594                     }
595                     if (mDefaultFilter != null && mDefaultFilter.uiReady()) {
596                         mDefaultFilter.dispose();
597                     }
598 
599                     // for each filter, create a tab.
600                     int i = 0;
601                     if (mFilters != null) {
602                         for (LogFilter f : mFilters) {
603                             createTab(f, i++, true);
604                         }
605                     }
606                     if (mDefaultFilter != null) {
607                         createTab(mDefaultFilter, i++, true);
608                     }
609                 } else {
610 
611                     // create ui for the filter.
612                     createTab(filter, index, true);
613 
614                     // reset the default as it shouldn't contain the content of
615                     // this new filter.
616                     if (mDefaultFilter != null) {
617                         initDefaultFilter();
618                     }
619                 }
620 
621                 // select the new filter
622                 if (mCurrentFilter != null) {
623                     mCurrentFilter.setSelectedState(false);
624                 }
625                 mFolders.setSelection(index);
626                 filter.setSelectedState(true);
627                 mCurrentFilter = filter;
628 
629                 selectionChanged(filter);
630 
631                 // finally we update the filtering mode if needed
632                 if (mFilterMode == FILTER_NONE) {
633                     mFilterMode = FILTER_MANUAL;
634                 }
635 
636                 mFilterStorage.saveFilters(mFilters);
637 
638             }
639         }
640     }
641 
642     /**
643      * Edits the current filter. The method displays the UI to edit the filter.
644      */
editFilter()645     public void editFilter() {
646         if (mCurrentFilter != null && mCurrentFilter != mDefaultFilter) {
647             EditFilterDialog dlg = new EditFilterDialog(
648                     mFolders.getShell(), mCurrentFilter);
649             if (dlg.open()) {
650                 synchronized (mBuffer) {
651                     // at this point the filter has been updated.
652                     // so we update its content
653                     initFilter(mCurrentFilter);
654 
655                     // and the content of the "other" filter as well.
656                     if (mDefaultFilter != null) {
657                         initDefaultFilter();
658                     }
659 
660                     mFilterStorage.saveFilters(mFilters);
661                 }
662             }
663         }
664     }
665 
666     /**
667      * Deletes the current filter.
668      */
deleteFilter()669     public void deleteFilter() {
670         synchronized (mBuffer) {
671             if (mCurrentFilter != null && mCurrentFilter != mDefaultFilter) {
672                 // remove the filter from the list
673                 removeFilterFromArray(mCurrentFilter);
674                 mCurrentFilter.dispose();
675 
676                 // select the new filter
677                 mFolders.setSelection(0);
678                 if (mFilters.length > 0) {
679                     mCurrentFilter = mFilters[0];
680                 } else {
681                     mCurrentFilter = mDefaultFilter;
682                 }
683 
684                 selectionChanged(mCurrentFilter);
685 
686                 // update the content of the "other" filter to include what was filtered out
687                 // by the deleted filter.
688                 if (mDefaultFilter != null) {
689                     initDefaultFilter();
690                 }
691 
692                 mFilterStorage.saveFilters(mFilters);
693             }
694         }
695     }
696 
697     /**
698      * saves the current selection in a text file.
699      * @return false if the saving failed.
700      */
save()701     public boolean save() {
702         synchronized (mBuffer) {
703             FileDialog dlg = new FileDialog(mParent.getShell(), SWT.SAVE);
704             String fileName;
705 
706             dlg.setText("Save log...");
707             dlg.setFileName("log.txt");
708             String defaultPath = mDefaultLogSave;
709             if (defaultPath == null) {
710                 defaultPath = System.getProperty("user.home"); //$NON-NLS-1$
711             }
712             dlg.setFilterPath(defaultPath);
713             dlg.setFilterNames(new String[] {
714                 "Text Files (*.txt)"
715             });
716             dlg.setFilterExtensions(new String[] {
717                 "*.txt"
718             });
719 
720             fileName = dlg.open();
721             if (fileName != null) {
722                 mDefaultLogSave = dlg.getFilterPath();
723 
724                 // get the current table and its selection
725                 Table currentTable = mCurrentFilter.getTable();
726 
727                 int[] selection = currentTable.getSelectionIndices();
728 
729                 // we need to sort the items to be sure.
730                 Arrays.sort(selection);
731 
732                 // loop on the selection and output the file.
733                 try {
734                     FileWriter writer = new FileWriter(fileName);
735 
736                     for (int i : selection) {
737                         TableItem item = currentTable.getItem(i);
738                         LogMessage msg = (LogMessage)item.getData();
739                         String line = msg.toString();
740                         writer.write(line);
741                         writer.write('\n');
742                     }
743                     writer.flush();
744 
745                 } catch (IOException e) {
746                     return false;
747                 }
748             }
749         }
750 
751         return true;
752     }
753 
754     /**
755      * Empty the current circular buffer.
756      */
clear()757     public void clear() {
758         synchronized (mBuffer) {
759             for (int i = 0 ; i < STRING_BUFFER_LENGTH; i++) {
760                 mBuffer[i] = null;
761             }
762 
763             mBufferStart = -1;
764             mBufferEnd = -1;
765 
766             // now we clear the existing filters
767             for (LogFilter filter : mFilters) {
768                 filter.clear();
769             }
770 
771             // and the default one
772             if (mDefaultFilter != null) {
773                 mDefaultFilter.clear();
774             }
775         }
776     }
777 
778     /**
779      * Copies the current selection of the current filter as multiline text.
780      *
781      * @param clipboard The clipboard to place the copied content.
782      */
copy(Clipboard clipboard)783     public void copy(Clipboard clipboard) {
784         // get the current table and its selection
785         Table currentTable = mCurrentFilter.getTable();
786 
787         copyTable(clipboard, currentTable);
788     }
789 
790     /**
791      * Selects all lines.
792      */
selectAll()793     public void selectAll() {
794         Table currentTable = mCurrentFilter.getTable();
795         currentTable.selectAll();
796     }
797 
798     /**
799      * Sets a TableFocusListener which will be notified when one of the tables
800      * gets or loses focus.
801      *
802      * @param listener
803      */
setTableFocusListener(ITableFocusListener listener)804     public void setTableFocusListener(ITableFocusListener listener) {
805         // record the global listener, to make sure table created after
806         // this call will still be setup.
807         mGlobalListener = listener;
808 
809         // now we setup the existing filters
810         for (LogFilter filter : mFilters) {
811             Table table = filter.getTable();
812 
813             addTableToFocusListener(table);
814         }
815 
816         // and the default one
817         if (mDefaultFilter != null) {
818             addTableToFocusListener(mDefaultFilter.getTable());
819         }
820     }
821 
822     /**
823      * Sets up a Table object to notify the global Table Focus listener when it
824      * gets or loses the focus.
825      *
826      * @param table the Table object.
827      */
addTableToFocusListener(final Table table)828     private void addTableToFocusListener(final Table table) {
829         // create the activator for this table
830         final IFocusedTableActivator activator = new IFocusedTableActivator() {
831             @Override
832             public void copy(Clipboard clipboard) {
833                 copyTable(clipboard, table);
834             }
835 
836             @Override
837             public void selectAll() {
838                 table.selectAll();
839             }
840         };
841 
842         // add the focus listener on the table to notify the global listener
843         table.addFocusListener(new FocusListener() {
844             @Override
845             public void focusGained(FocusEvent e) {
846                 mGlobalListener.focusGained(activator);
847             }
848 
849             @Override
850             public void focusLost(FocusEvent e) {
851                 mGlobalListener.focusLost(activator);
852             }
853         });
854     }
855 
856     /**
857      * Copies the current selection of a Table into the provided Clipboard, as
858      * multi-line text.
859      *
860      * @param clipboard The clipboard to place the copied content.
861      * @param table The table to copy from.
862      */
copyTable(Clipboard clipboard, Table table)863     private static void copyTable(Clipboard clipboard, Table table) {
864         int[] selection = table.getSelectionIndices();
865 
866         // we need to sort the items to be sure.
867         Arrays.sort(selection);
868 
869         // all lines must be concatenated.
870         StringBuilder sb = new StringBuilder();
871 
872         // loop on the selection and output the file.
873         for (int i : selection) {
874             TableItem item = table.getItem(i);
875             LogMessage msg = (LogMessage)item.getData();
876             String line = msg.toString();
877             sb.append(line);
878             sb.append('\n');
879         }
880 
881         // now add that to the clipboard
882         clipboard.setContents(new Object[] {
883             sb.toString()
884         }, new Transfer[] {
885             TextTransfer.getInstance()
886         });
887     }
888 
889     /**
890      * Sets the log level for the current filter, but does not save it.
891      * @param i
892      */
setCurrentFilterLogLevel(int i)893     public void setCurrentFilterLogLevel(int i) {
894         LogFilter filter = getCurrentFilter();
895 
896         filter.setLogLevel(i);
897 
898         initFilter(filter);
899     }
900 
901     /**
902      * Creates a new tab in the folderTab item. Must be called from the ui
903      *      thread.
904      * @param filter The filter associated with the tab.
905      * @param index the index of the tab. if -1, the tab will be added at the
906      *          end.
907      * @param fillTable If true the table is filled with the current content of
908      *          the buffer.
909      * @return The TabItem object that was created.
910      */
createTab(LogFilter filter, int index, boolean fillTable)911     private TabItem createTab(LogFilter filter, int index, boolean fillTable) {
912         synchronized (mBuffer) {
913             TabItem item = null;
914             if (index != -1) {
915                 item = new TabItem(mFolders, SWT.NONE, index);
916             } else {
917                 item = new TabItem(mFolders, SWT.NONE);
918             }
919             item.setText(filter.getName());
920 
921             // set the control (the parent is the TabFolder item, always)
922             Composite top = new Composite(mFolders, SWT.NONE);
923             item.setControl(top);
924 
925             top.setLayout(new FillLayout());
926 
927             // create the ui, first the table
928             final Table t = new Table(top, SWT.MULTI | SWT.FULL_SELECTION);
929             t.addSelectionListener(new SelectionAdapter() {
930                 @Override
931                 public void widgetDefaultSelected(SelectionEvent e) {
932                     if (mLogCatViewInterface != null) {
933                         mLogCatViewInterface.onDoubleClick();
934                     }
935                 }
936             });
937 
938             if (mDisplayFont != null) {
939                 t.setFont(mDisplayFont);
940             }
941 
942             // give the ui objects to the filters.
943             filter.setWidgets(item, t);
944 
945             t.setHeaderVisible(true);
946             t.setLinesVisible(false);
947 
948             if (mGlobalListener != null) {
949                 addTableToFocusListener(t);
950             }
951 
952             // create a controllistener that will handle the resizing of all the
953             // columns (except the last) and of the table itself.
954             ControlListener listener = null;
955             if (mColumnMode == COLUMN_MODE_AUTO) {
956                 listener = new ControlListener() {
957                     @Override
958                     public void controlMoved(ControlEvent e) {
959                     }
960 
961                     @Override
962                     public void controlResized(ControlEvent e) {
963                         Rectangle r = t.getClientArea();
964 
965                         // get the size of all but the last column
966                         int total = t.getColumn(0).getWidth();
967                         total += t.getColumn(1).getWidth();
968                         total += t.getColumn(2).getWidth();
969                         total += t.getColumn(3).getWidth();
970 
971                         if (r.width > total) {
972                             t.getColumn(4).setWidth(r.width-total);
973                         }
974                     }
975                 };
976 
977                 t.addControlListener(listener);
978             }
979 
980             // then its column
981             TableColumn col = TableHelper.createTableColumn(t, "Time", SWT.LEFT,
982                     "00-00 00:00:00", //$NON-NLS-1$
983                     PREFS_TIME, mStore);
984             if (mColumnMode == COLUMN_MODE_AUTO) {
985                 col.addControlListener(listener);
986             }
987 
988             col = TableHelper.createTableColumn(t, "", SWT.CENTER,
989                     "D", //$NON-NLS-1$
990                     PREFS_LEVEL, mStore);
991             if (mColumnMode == COLUMN_MODE_AUTO) {
992                 col.addControlListener(listener);
993             }
994 
995             col = TableHelper.createTableColumn(t, "pid", SWT.LEFT,
996                     "9999", //$NON-NLS-1$
997                     PREFS_PID, mStore);
998             if (mColumnMode == COLUMN_MODE_AUTO) {
999                 col.addControlListener(listener);
1000             }
1001 
1002             col = TableHelper.createTableColumn(t, "tag", SWT.LEFT,
1003                     "abcdefgh",  //$NON-NLS-1$
1004                     PREFS_TAG, mStore);
1005             if (mColumnMode == COLUMN_MODE_AUTO) {
1006                 col.addControlListener(listener);
1007             }
1008 
1009             col = TableHelper.createTableColumn(t, "Message", SWT.LEFT,
1010                     "abcdefghijklmnopqrstuvwxyz0123456789",  //$NON-NLS-1$
1011                     PREFS_MESSAGE, mStore);
1012             if (mColumnMode == COLUMN_MODE_AUTO) {
1013                 // instead of listening on resize for the last column, we make
1014                 // it non resizable.
1015                 col.setResizable(false);
1016             }
1017 
1018             if (fillTable) {
1019                 initFilter(filter);
1020             }
1021             return item;
1022         }
1023     }
1024 
updateColumns(Table table)1025     protected void updateColumns(Table table) {
1026         if (table != null) {
1027             int index = 0;
1028             TableColumn col;
1029 
1030             col = table.getColumn(index++);
1031             col.setWidth(mStore.getInt(PREFS_TIME));
1032 
1033             col = table.getColumn(index++);
1034             col.setWidth(mStore.getInt(PREFS_LEVEL));
1035 
1036             col = table.getColumn(index++);
1037             col.setWidth(mStore.getInt(PREFS_PID));
1038 
1039             col = table.getColumn(index++);
1040             col.setWidth(mStore.getInt(PREFS_TAG));
1041 
1042             col = table.getColumn(index++);
1043             col.setWidth(mStore.getInt(PREFS_MESSAGE));
1044         }
1045     }
1046 
resetUI(boolean inUiThread)1047     public void resetUI(boolean inUiThread) {
1048         if (mFilterMode == FILTER_AUTO_PID || mFilterMode == FILTER_AUTO_TAG) {
1049             if (inUiThread) {
1050                 mFolders.dispose();
1051                 mParent.pack(true);
1052                 createControl(mParent);
1053             } else {
1054                 Display d = mFolders.getDisplay();
1055 
1056                 // run sync as we need to update right now.
1057                 d.syncExec(new Runnable() {
1058                     @Override
1059                     public void run() {
1060                         mFolders.dispose();
1061                         mParent.pack(true);
1062                         createControl(mParent);
1063                     }
1064                 });
1065             }
1066         } else  {
1067             // the ui is static we just empty it.
1068             if (mFolders.isDisposed() == false) {
1069                 if (inUiThread) {
1070                     emptyTables();
1071                 } else {
1072                     Display d = mFolders.getDisplay();
1073 
1074                     // run sync as we need to update right now.
1075                     d.syncExec(new Runnable() {
1076                         @Override
1077                         public void run() {
1078                             if (mFolders.isDisposed() == false) {
1079                                 emptyTables();
1080                             }
1081                         }
1082                     });
1083                 }
1084             }
1085         }
1086     }
1087 
1088     /**
1089      * Process new Log lines coming from {@link LogCatOuputReceiver}.
1090      * @param lines the new lines
1091      */
processLogLines(String[] lines)1092     protected void processLogLines(String[] lines) {
1093         // WARNING: this will not work if the string contains more line than
1094         // the buffer holds.
1095 
1096         if (lines.length > STRING_BUFFER_LENGTH) {
1097             Log.e("LogCat", "Receiving more lines than STRING_BUFFER_LENGTH");
1098         }
1099 
1100         // parse the lines and create LogMessage that are stored in a temporary list
1101         final ArrayList<LogMessage> newMessages = new ArrayList<LogMessage>();
1102 
1103         synchronized (mBuffer) {
1104             for (String line : lines) {
1105                 // ignore empty lines.
1106                 if (line.length() > 0) {
1107                     // check for header lines.
1108                     Matcher matcher = sLogPattern.matcher(line);
1109                     if (matcher.matches()) {
1110                         // this is a header line, parse the header and keep it around.
1111                         mLastMessageInfo = new LogMessageInfo();
1112 
1113                         mLastMessageInfo.time = matcher.group(1);
1114                         mLastMessageInfo.pidString = matcher.group(2);
1115                         mLastMessageInfo.pid = Integer.valueOf(mLastMessageInfo.pidString);
1116                         mLastMessageInfo.logLevel = LogLevel.getByLetterString(matcher.group(4));
1117                         mLastMessageInfo.tag = matcher.group(5).trim();
1118                     } else {
1119                         // This is not a header line.
1120                         // Create a new LogMessage and process it.
1121                         LogMessage mc = new LogMessage();
1122 
1123                         if (mLastMessageInfo == null) {
1124                             // The first line of output wasn't preceded
1125                             // by a header line; make something up so
1126                             // that users of mc.data don't NPE.
1127                             mLastMessageInfo = new LogMessageInfo();
1128                             mLastMessageInfo.time = "??-?? ??:??:??.???"; //$NON-NLS1$
1129                             mLastMessageInfo.pidString = "<unknown>"; //$NON-NLS1$
1130                             mLastMessageInfo.pid = 0;
1131                             mLastMessageInfo.logLevel = LogLevel.INFO;
1132                             mLastMessageInfo.tag = "<unknown>"; //$NON-NLS1$
1133                         }
1134 
1135                         // If someone printed a log message with
1136                         // embedded '\n' characters, there will
1137                         // one header line followed by multiple text lines.
1138                         // Use the last header that we saw.
1139                         mc.data = mLastMessageInfo;
1140 
1141                         // tabs seem to display as only 1 tab so we replace the leading tabs
1142                         // by 4 spaces.
1143                         mc.msg = line.replaceAll("\t", "    "); //$NON-NLS-1$ //$NON-NLS-2$
1144 
1145                         // process the new LogMessage.
1146                         processNewMessage(mc);
1147 
1148                         // store the new LogMessage
1149                         newMessages.add(mc);
1150                     }
1151                 }
1152             }
1153 
1154             // if we don't have a pending Runnable that will do the refresh, we ask the Display
1155             // to run one in the UI thread.
1156             if (mPendingAsyncRefresh == false) {
1157                 mPendingAsyncRefresh = true;
1158 
1159                 try {
1160                     Display display = mFolders.getDisplay();
1161 
1162                     // run in sync because this will update the buffer start/end indices
1163                     display.asyncExec(new Runnable() {
1164                         @Override
1165                         public void run() {
1166                             asyncRefresh();
1167                         }
1168                     });
1169                 } catch (SWTException e) {
1170                     // display is disposed, we're probably quitting. Let's stop.
1171                     stopLogCat(false);
1172                 }
1173             }
1174         }
1175     }
1176 
1177     /**
1178      * Refreshes the UI with new messages.
1179      */
asyncRefresh()1180     private void asyncRefresh() {
1181         if (mFolders.isDisposed() == false) {
1182             synchronized (mBuffer) {
1183                 try {
1184                     // the circular buffer has been updated, let have the filter flush their
1185                     // display with the new messages.
1186                     if (mFilters != null) {
1187                         for (LogFilter f : mFilters) {
1188                             f.flush();
1189                         }
1190                     }
1191 
1192                     if (mDefaultFilter != null) {
1193                         mDefaultFilter.flush();
1194                     }
1195                 } finally {
1196                     // the pending refresh is done.
1197                     mPendingAsyncRefresh = false;
1198                 }
1199             }
1200         } else {
1201             stopLogCat(true);
1202         }
1203     }
1204 
1205     /**
1206      * Processes a new Message.
1207      * <p/>This adds the new message to the buffer, and gives it to the existing filters.
1208      * @param newMessage
1209      */
processNewMessage(LogMessage newMessage)1210     private void processNewMessage(LogMessage newMessage) {
1211         // if we are in auto filtering mode, make sure we have
1212         // a filter for this
1213         if (mFilterMode == FILTER_AUTO_PID ||
1214                 mFilterMode == FILTER_AUTO_TAG) {
1215            checkFilter(newMessage.data);
1216         }
1217 
1218         // compute the index where the message goes.
1219         // was the buffer empty?
1220         int messageIndex = -1;
1221         if (mBufferStart == -1) {
1222             messageIndex = mBufferStart = 0;
1223             mBufferEnd = 1;
1224         } else {
1225             messageIndex = mBufferEnd;
1226 
1227             // increment the next usable slot index
1228             mBufferEnd = (mBufferEnd + 1) % STRING_BUFFER_LENGTH;
1229 
1230             // check we aren't overwriting start
1231             if (mBufferEnd == mBufferStart) {
1232                 mBufferStart = (mBufferStart + 1) % STRING_BUFFER_LENGTH;
1233             }
1234         }
1235 
1236         LogMessage oldMessage = null;
1237 
1238         // record the message that was there before
1239         if (mBuffer[messageIndex] != null) {
1240             oldMessage = mBuffer[messageIndex];
1241         }
1242 
1243         // then add the new one
1244         mBuffer[messageIndex] = newMessage;
1245 
1246         // give the new message to every filters.
1247         boolean filtered = false;
1248         if (mFilters != null) {
1249             for (LogFilter f : mFilters) {
1250                 filtered |= f.addMessage(newMessage, oldMessage);
1251             }
1252         }
1253         if (filtered == false && mDefaultFilter != null) {
1254             mDefaultFilter.addMessage(newMessage, oldMessage);
1255         }
1256     }
1257 
createFilters()1258     private void createFilters() {
1259         if (mFilterMode == FILTER_DEBUG || mFilterMode == FILTER_MANUAL) {
1260             // unarchive the filters.
1261             mFilters = mFilterStorage.getFilterFromStore();
1262 
1263             // set the colors
1264             if (mFilters != null) {
1265                 for (LogFilter f : mFilters) {
1266                     f.setColors(mColors);
1267                 }
1268             }
1269 
1270             if (mFilterStorage.requiresDefaultFilter()) {
1271                 mDefaultFilter = new LogFilter("Log");
1272                 mDefaultFilter.setColors(mColors);
1273                 mDefaultFilter.setSupportsDelete(false);
1274                 mDefaultFilter.setSupportsEdit(false);
1275             }
1276         } else if (mFilterMode == FILTER_NONE) {
1277             // if the filtering mode is "none", we create a single filter that
1278             // will receive all
1279             mDefaultFilter = new LogFilter("Log");
1280             mDefaultFilter.setColors(mColors);
1281             mDefaultFilter.setSupportsDelete(false);
1282             mDefaultFilter.setSupportsEdit(false);
1283         }
1284     }
1285 
1286     /** Checks if there's an automatic filter for this md and if not
1287      * adds the filter and the ui.
1288      * This must be called from the UI!
1289      * @param md
1290      * @return true if the filter existed already
1291      */
checkFilter(final LogMessageInfo md)1292     private boolean checkFilter(final LogMessageInfo md) {
1293         if (true)
1294             return true;
1295         // look for a filter that matches the pid
1296         if (mFilterMode == FILTER_AUTO_PID) {
1297             for (LogFilter f : mFilters) {
1298                 if (f.getPidFilter() == md.pid) {
1299                     return true;
1300                 }
1301             }
1302         } else if (mFilterMode == FILTER_AUTO_TAG) {
1303             for (LogFilter f : mFilters) {
1304                 if (f.getTagFilter().equals(md.tag)) {
1305                     return true;
1306                 }
1307             }
1308         }
1309 
1310         // if we reach this point, no filter was found.
1311         // create a filter with a temporary name of the pid
1312         final LogFilter newFilter = new LogFilter(md.pidString);
1313         String name = null;
1314         if (mFilterMode == FILTER_AUTO_PID) {
1315             newFilter.setPidMode(md.pid);
1316 
1317             // ask the monitor thread if it knows the pid.
1318             name = mCurrentLoggedDevice.getClientName(md.pid);
1319         } else {
1320             newFilter.setTagMode(md.tag);
1321             name = md.tag;
1322         }
1323         addFilterToArray(newFilter);
1324 
1325         final String fname = name;
1326 
1327         // create the tabitem
1328         final TabItem newTabItem = createTab(newFilter, -1, true);
1329 
1330         // if the name is unknown
1331         if (fname == null) {
1332             // we need to find the process running under that pid.
1333             // launch a thread do a ps on the device
1334             new Thread("remote PS") { //$NON-NLS-1$
1335                 @Override
1336                 public void run() {
1337                     // create the receiver
1338                     PsOutputReceiver psor = new PsOutputReceiver(md.pid,
1339                             newFilter, newTabItem);
1340 
1341                     // execute ps
1342                     try {
1343                         mCurrentLoggedDevice.executeShellCommand("ps", psor); //$NON-NLS-1$
1344                     } catch (IOException e) {
1345                         // Ignore
1346                     } catch (TimeoutException e) {
1347                         // Ignore
1348                     } catch (AdbCommandRejectedException e) {
1349                         // Ignore
1350                     } catch (ShellCommandUnresponsiveException e) {
1351                         // Ignore
1352                     }
1353                 }
1354             }.start();
1355         }
1356 
1357         return false;
1358     }
1359 
1360     /**
1361      * Adds a new filter to the current filter array, and set its colors
1362      * @param newFilter The filter to add
1363      */
addFilterToArray(LogFilter newFilter)1364     private void addFilterToArray(LogFilter newFilter) {
1365         // set the colors
1366         newFilter.setColors(mColors);
1367 
1368         // add it to the array.
1369         if (mFilters != null && mFilters.length > 0) {
1370             LogFilter[] newFilters = new LogFilter[mFilters.length+1];
1371             System.arraycopy(mFilters, 0, newFilters, 0, mFilters.length);
1372             newFilters[mFilters.length] = newFilter;
1373             mFilters = newFilters;
1374         } else {
1375             mFilters = new LogFilter[1];
1376             mFilters[0] = newFilter;
1377         }
1378     }
1379 
removeFilterFromArray(LogFilter oldFilter)1380     private void removeFilterFromArray(LogFilter oldFilter) {
1381         // look for the index
1382         int index = -1;
1383         for (int i = 0 ; i < mFilters.length ; i++) {
1384             if (mFilters[i] == oldFilter) {
1385                 index = i;
1386                 break;
1387             }
1388         }
1389 
1390         if (index != -1) {
1391             LogFilter[] newFilters = new LogFilter[mFilters.length-1];
1392             System.arraycopy(mFilters, 0, newFilters, 0, index);
1393             System.arraycopy(mFilters, index + 1, newFilters, index,
1394                     newFilters.length-index);
1395             mFilters = newFilters;
1396         }
1397     }
1398 
1399     /**
1400      * Initialize the filter with already existing buffer.
1401      * @param filter
1402      */
initFilter(LogFilter filter)1403     private void initFilter(LogFilter filter) {
1404         // is it empty
1405         if (filter.uiReady() == false) {
1406             return;
1407         }
1408 
1409         if (filter == mDefaultFilter) {
1410             initDefaultFilter();
1411             return;
1412         }
1413 
1414         filter.clear();
1415 
1416         if (mBufferStart != -1) {
1417             int max = mBufferEnd;
1418             if (mBufferEnd < mBufferStart) {
1419                 max += STRING_BUFFER_LENGTH;
1420             }
1421 
1422             for (int i = mBufferStart; i < max; i++) {
1423                 int realItemIndex = i % STRING_BUFFER_LENGTH;
1424 
1425                 filter.addMessage(mBuffer[realItemIndex], null /* old message */);
1426             }
1427         }
1428 
1429         filter.flush();
1430         filter.resetTempFilteringStatus();
1431     }
1432 
1433     /**
1434      * Refill the default filter. Not to be called directly.
1435      * @see initFilter()
1436      */
initDefaultFilter()1437     private void initDefaultFilter() {
1438         mDefaultFilter.clear();
1439 
1440         if (mBufferStart != -1) {
1441             int max = mBufferEnd;
1442             if (mBufferEnd < mBufferStart) {
1443                 max += STRING_BUFFER_LENGTH;
1444             }
1445 
1446             for (int i = mBufferStart; i < max; i++) {
1447                 int realItemIndex = i % STRING_BUFFER_LENGTH;
1448                 LogMessage msg = mBuffer[realItemIndex];
1449 
1450                 // first we check that the other filters don't take this message
1451                 boolean filtered = false;
1452                 for (LogFilter f : mFilters) {
1453                     filtered |= f.accept(msg);
1454                 }
1455 
1456                 if (filtered == false) {
1457                     mDefaultFilter.addMessage(msg, null /* old message */);
1458                 }
1459             }
1460         }
1461 
1462         mDefaultFilter.flush();
1463         mDefaultFilter.resetTempFilteringStatus();
1464     }
1465 
1466     /**
1467      * Reset the filters, to handle change in device in automatic filter mode
1468      */
resetFilters()1469     private void resetFilters() {
1470         // if we are in automatic mode, then we need to rmove the current
1471         // filter.
1472         if (mFilterMode == FILTER_AUTO_PID || mFilterMode == FILTER_AUTO_TAG) {
1473             mFilters = null;
1474 
1475             // recreate the filters.
1476             createFilters();
1477         }
1478     }
1479 
1480 
getCurrentFilter()1481     private LogFilter getCurrentFilter() {
1482         int index = mFolders.getSelectionIndex();
1483 
1484         // if mFilters is null or index is invalid, we return the default
1485         // filter. It doesn't matter if that one is null as well, since we
1486         // would return null anyway.
1487         if (index == 0 || mFilters == null) {
1488             return mDefaultFilter;
1489         }
1490 
1491         return mFilters[index-1];
1492     }
1493 
1494 
emptyTables()1495     private void emptyTables() {
1496         for (LogFilter f : mFilters) {
1497             f.getTable().removeAll();
1498         }
1499 
1500         if (mDefaultFilter != null) {
1501             mDefaultFilter.getTable().removeAll();
1502         }
1503     }
1504 
updateFilteringWith(String text)1505     protected void updateFilteringWith(String text) {
1506         synchronized (mBuffer) {
1507             // reset the temp filtering for all the filters
1508             for (LogFilter f : mFilters) {
1509                 f.resetTempFiltering();
1510             }
1511             if (mDefaultFilter != null) {
1512                 mDefaultFilter.resetTempFiltering();
1513             }
1514 
1515             // now we need to figure out the new temp filtering
1516             // split each word
1517             String[] segments = text.split(" "); //$NON-NLS-1$
1518 
1519             ArrayList<String> keywords = new ArrayList<String>(segments.length);
1520 
1521             // loop and look for temp id/tag
1522             int tempPid = -1;
1523             String tempTag = null;
1524             for (int i = 0 ; i < segments.length; i++) {
1525                 String s = segments[i];
1526                 if (tempPid == -1 && s.startsWith("pid:")) { //$NON-NLS-1$
1527                     // get the pid
1528                     String[] seg = s.split(":"); //$NON-NLS-1$
1529                     if (seg.length == 2) {
1530                         if (seg[1].matches("^[0-9]*$")) { //$NON-NLS-1$
1531                             tempPid = Integer.valueOf(seg[1]);
1532                         }
1533                     }
1534                 } else if (tempTag == null && s.startsWith("tag:")) { //$NON-NLS-1$
1535                     String seg[] = segments[i].split(":"); //$NON-NLS-1$
1536                     if (seg.length == 2) {
1537                         tempTag = seg[1];
1538                     }
1539                 } else {
1540                     keywords.add(s);
1541                 }
1542             }
1543 
1544             // set the temp filtering in the filters
1545             if (tempPid != -1 || tempTag != null || keywords.size() > 0) {
1546                 String[] keywordsArray = keywords.toArray(
1547                         new String[keywords.size()]);
1548 
1549                 for (LogFilter f : mFilters) {
1550                     if (tempPid != -1) {
1551                         f.setTempPidFiltering(tempPid);
1552                     }
1553                     if (tempTag != null) {
1554                         f.setTempTagFiltering(tempTag);
1555                     }
1556                     f.setTempKeywordFiltering(keywordsArray);
1557                 }
1558 
1559                 if (mDefaultFilter != null) {
1560                     if (tempPid != -1) {
1561                         mDefaultFilter.setTempPidFiltering(tempPid);
1562                     }
1563                     if (tempTag != null) {
1564                         mDefaultFilter.setTempTagFiltering(tempTag);
1565                     }
1566                     mDefaultFilter.setTempKeywordFiltering(keywordsArray);
1567 
1568                 }
1569             }
1570 
1571             initFilter(mCurrentFilter);
1572         }
1573     }
1574 
1575     /**
1576      * Called when the current filter selection changes.
1577      * @param selectedFilter
1578      */
selectionChanged(LogFilter selectedFilter)1579     private void selectionChanged(LogFilter selectedFilter) {
1580         if (mLogLevelActions != null) {
1581             // get the log level
1582             int level = selectedFilter.getLogLevel();
1583             for (int i = 0 ; i < mLogLevelActions.length; i++) {
1584                 ICommonAction a = mLogLevelActions[i];
1585                 if (i == level - 2) {
1586                     a.setChecked(true);
1587                 } else {
1588                     a.setChecked(false);
1589                 }
1590             }
1591         }
1592 
1593         if (mDeleteFilterAction != null) {
1594             mDeleteFilterAction.setEnabled(selectedFilter.supportsDelete());
1595         }
1596         if (mEditFilterAction != null) {
1597             mEditFilterAction.setEnabled(selectedFilter.supportsEdit());
1598         }
1599     }
1600 
getSelectedErrorLineMessage()1601     public String getSelectedErrorLineMessage() {
1602         Table table = mCurrentFilter.getTable();
1603         int[] selection = table.getSelectionIndices();
1604 
1605         if (selection.length == 1) {
1606             TableItem item = table.getItem(selection[0]);
1607             LogMessage msg = (LogMessage)item.getData();
1608             if (msg.data.logLevel == LogLevel.ERROR || msg.data.logLevel == LogLevel.WARN)
1609                 return msg.msg;
1610         }
1611         return null;
1612     }
1613 
setLogCatViewInterface(LogCatViewInterface i)1614     public void setLogCatViewInterface(LogCatViewInterface i) {
1615         mLogCatViewInterface = i;
1616     }
1617 }
1618