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