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