/* * Copyright 2000-2012 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jetbrains.android.logcat; import com.android.ddmlib.Log; import com.intellij.CommonBundle; import com.intellij.codeInsight.completion.CompletionParameters; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.editor.event.*; import com.intellij.openapi.editor.event.DocumentAdapter; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Splitter; import com.intellij.openapi.ui.ValidationInfo; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.*; import com.intellij.ui.components.JBLabel; import com.intellij.ui.components.JBList; import com.intellij.util.IconUtil; import com.intellij.util.containers.HashSet; import org.jetbrains.android.logcat.AndroidLogcatReceiver.LogMessageHeader; import org.jetbrains.android.util.AndroidBundle; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.*; import java.util.List; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; /** * @author Eugene.Kudelevsky */ class EditLogFilterDialog extends DialogWrapper { @NonNls private static final Key<JComponent> OWNER = new Key<JComponent>("Owner"); private static final String NEW_FILTER_NAME_PREFIX = "Unnamed-"; @NonNls private static final String EDIT_FILTER_DIALOG_DIMENSIONS_KEY = "edit.logcat.filter.dialog.dimensions"; private final Splitter mySplitter; private JPanel myContentPanel; private JPanel myLeftPanel; private EditorTextField myFilterNameField; private EditorTextField myLogMessageField; private TextFieldWithAutoCompletion myTagField; private TextFieldWithAutoCompletion myPidField; private TextFieldWithAutoCompletion myPackageNameField; private JComboBox myLogLevelCombo; private JPanel myTagFieldWrapper; private JPanel myPidFieldWrapper; private JPanel myPackageNameFieldWrapper; private JBLabel myNameFieldLabel; private JBLabel myLogTagLabel; private JBLabel myLogMessageLabel; private JBLabel myPidLabel; private JBLabel myPackageNameLabel; private JBList myFiltersList; private CollectionListModel<String> myFiltersListModel; private AndroidConfiguredLogFilters.MyFilterEntry mySelectedEntry; private final List<AndroidConfiguredLogFilters.MyFilterEntry> myFilterEntries; private JPanel myFiltersToolbarPanel; private final AndroidLogcatView myView; private final Project myProject; private boolean myExistingMessagesParsed = false; private String[] myUsedTags; private String[] myUsedPids; private String[] myUsedPackageNames; protected EditLogFilterDialog(@NotNull final AndroidLogcatView view, @Nullable String selectedFilter) { super(view.getProject(), false); myView = view; myProject = view.getProject(); mySplitter = new Splitter(false, 0.25f); mySplitter.setFirstComponent(myLeftPanel); mySplitter.setSecondComponent(myContentPanel); myFilterEntries = AndroidConfiguredLogFilters.getInstance(myProject).getFilterEntries(); if (selectedFilter != null) { for (AndroidConfiguredLogFilters.MyFilterEntry fe: myFilterEntries) { if (selectedFilter.equals(fe.getName())) { mySelectedEntry = fe; } } } if (mySelectedEntry == null) { mySelectedEntry = myFilterEntries.isEmpty() ? createNewFilterEntry() : myFilterEntries.get(0); } createEditorFields(); initFiltersToolbar(); initFiltersList(); updateConfiguredFilters(); init(); } @Override @Nullable @NonNls protected String getDimensionServiceKey() { return EDIT_FILTER_DIALOG_DIMENSIONS_KEY; } private void createEditorFields() { myNameFieldLabel.setLabelFor(myFilterNameField); myLogMessageLabel.setLabelFor(myLogMessageField); myTagField = new TextFieldWithAutoCompletion<String>(myProject, new TextFieldWithAutoCompletion.StringsCompletionProvider(null, null) { @NotNull @Override public Collection<String> getItems(String prefix, boolean cached, CompletionParameters parameters) { parseExistingMessagesIfNecessary(); setItems(Arrays.asList(myUsedTags)); return super.getItems(prefix, cached, parameters); } }, true, null); myTagFieldWrapper.add(myTagField); myLogTagLabel.setLabelFor(myTagField); myPidField = new TextFieldWithAutoCompletion<String>(myProject, new TextFieldWithAutoCompletion.StringsCompletionProvider(null, null) { @NotNull @Override public Collection<String> getItems(String prefix, boolean cached, CompletionParameters parameters) { parseExistingMessagesIfNecessary(); setItems(Arrays.asList(myUsedPids)); return super.getItems(prefix, cached, parameters); } @Override public int compare(String item1, String item2) { final int pid1 = Integer.parseInt(item1); final int pid2 = Integer.parseInt(item2); return Comparing.compare(pid1, pid2); } }, true, null); myPidFieldWrapper.add(myPidField); myPidLabel.setLabelFor(myPidField); myPackageNameField = new TextFieldWithAutoCompletion<String>( myProject, new TextFieldWithAutoCompletion.StringsCompletionProvider(null, null) { @NotNull @Override public Collection<String> getItems(String prefix, boolean cached, CompletionParameters parameters) { parseExistingMessagesIfNecessary(); setItems(Arrays.asList(myUsedPackageNames)); return super.getItems(prefix, cached, parameters); } }, true, null); myPackageNameFieldWrapper.add(myPackageNameField); myPackageNameLabel.setLabelFor(myPackageNameField); myLogLevelCombo.setModel(new EnumComboBoxModel<Log.LogLevel>(Log.LogLevel.class)); myLogLevelCombo.setRenderer(new ListCellRendererWrapper() { @Override public void customize(JList list, Object value, int index, boolean selected, boolean hasFocus) { if (value != null) { setText(StringUtil.capitalize(((Log.LogLevel)value).getStringValue().toLowerCase())); } } }); myLogLevelCombo.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (mySelectedEntry == null) { return; } Log.LogLevel selectedItem = (Log.LogLevel)myLogLevelCombo.getSelectedItem(); mySelectedEntry.setLogLevel(selectedItem.getStringValue()); } }); myFilterNameField.getDocument().putUserData(OWNER, myFilterNameField); myTagField.getDocument().putUserData(OWNER, myTagField); myLogMessageField.getDocument().putUserData(OWNER, myLogMessageField); myPidField.getDocument().putUserData(OWNER, myPidField); myPackageNameField.getDocument().putUserData(OWNER, myPackageNameField); DocumentListener l = new DocumentAdapter() { @Override public void documentChanged(DocumentEvent e) { if (mySelectedEntry == null) { return; } String text = e.getDocument().getText().trim(); JComponent src = e.getDocument().getUserData(OWNER); if (src == myTagField) { mySelectedEntry.setLogTagPattern(text); } else if (src == myPidField) { mySelectedEntry.setPid(text); } else if (src == myPackageNameField) { mySelectedEntry.setPackageNamePattern(text); } else if (src == myLogMessageField) { mySelectedEntry.setLogMessagePattern(text); } else if (src == myFilterNameField) { int index = myFiltersList.getSelectedIndex(); if (index != -1) { myFiltersListModel.setElementAt(text, index); } mySelectedEntry.setName(text); } } }; myFilterNameField.getDocument().addDocumentListener(l); myTagField.getDocument().addDocumentListener(l); myLogMessageField.getDocument().addDocumentListener(l); myPidField.getDocument().addDocumentListener(l); myPackageNameField.getDocument().addDocumentListener(l); } private void initFiltersList() { myFiltersList.addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { if (e.getValueIsAdjusting()) { return; } int i = myFiltersList.getSelectedIndex(); mySelectedEntry = (i == -1) ? null : myFilterEntries.get(i); resetFieldEditors(); } }); myFiltersListModel = new CollectionListModel<String>(); for (AndroidConfiguredLogFilters.MyFilterEntry entry : myFilterEntries) { myFiltersListModel.add(entry.getName()); } myFiltersList.setModel(myFiltersListModel); myFiltersList.setEmptyText( AndroidBundle.message("android.logcat.edit.filter.dialog.no.filters")); } private void initFiltersToolbar() { final DefaultActionGroup group = new DefaultActionGroup(); group.add(new MyAddFilterAction()); group.add(new MyRemoveFilterAction()); final JComponent component = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, group, true).getComponent(); myFiltersToolbarPanel.add(component, BorderLayout.CENTER); } private void parseExistingMessagesIfNecessary() { if (myExistingMessagesParsed) { return; } myExistingMessagesParsed = true; final StringBuffer document = myView.getLogConsole().getOriginalDocument(); if (document == null) { return; } final Set<String> tagSet = new HashSet<String>(); final Set<String> pidSet = new HashSet<String>(); final Set<String> pkgSet = new HashSet<String>(); final String[] lines = StringUtil.splitByLines(document.toString()); for (String line : lines) { Pair<LogMessageHeader,String> result = AndroidLogcatFormatter.parseMessage(line); if (result.getFirst() == null) { continue; } final String tag = result.getFirst().myTag; if (StringUtil.isNotEmpty(tag)) { tagSet.add(tag); } final String pkg = result.getFirst().myAppPackage; if (StringUtil.isNotEmpty(pkg)) { pkgSet.add(pkg); } pidSet.add(Integer.toString(result.getFirst().myPid)); } myUsedTags = tagSet.toArray(new String[tagSet.size()]); myUsedPids = pidSet.toArray(new String[pidSet.size()]); myUsedPackageNames = pkgSet.toArray(new String[pkgSet.size()]); } private void resetFieldEditors() { boolean enabled = mySelectedEntry != null; myFilterNameField.setEnabled(enabled); myTagField.setEnabled(enabled); myLogMessageField.setEnabled(enabled); myPidField.setEnabled(enabled); myPackageNameField.setEnabled(enabled); myLogLevelCombo.setEnabled(enabled); String name = enabled ? mySelectedEntry.getName() : ""; String tag = enabled ? mySelectedEntry.getLogTagPattern() : ""; String msg = enabled ? mySelectedEntry.getLogMessagePattern() : ""; String pid = enabled ? mySelectedEntry.getPid() : ""; String pkg = enabled ? mySelectedEntry.getPackageNamePattern() : ""; Log.LogLevel logLevel = enabled ? Log.LogLevel.getByString(mySelectedEntry.getLogLevel()) : Log.LogLevel.VERBOSE; myFilterNameField.setText(name != null ? name : ""); myTagField.setText(tag != null ? tag : ""); myLogMessageField.setText(msg != null ? msg : ""); myPidField.setText(pid != null ? pid : ""); myPackageNameField.setText(pkg != null ? pkg : ""); myLogLevelCombo.setSelectedItem(logLevel != null ? logLevel : Log.LogLevel.VERBOSE); } @Override protected JComponent createCenterPanel() { return mySplitter; } @Override public JComponent getPreferredFocusedComponent() { return myFilterNameField.getText().length() == 0 ? myFilterNameField : myTagField; } @Override protected void doOKAction() { AndroidConfiguredLogFilters.getInstance(myProject).setFilterEntries(myFilterEntries); super.doOKAction(); } @Override protected ValidationInfo doValidate() { if (!myFilterNameField.isEnabled()) { return null; } final String name = myFilterNameField.getText().trim(); if (name.isEmpty()) { return new ValidationInfo(AndroidBundle.message("android.logcat.new.filter.dialog.name.not.specified.error"), myFilterNameField); } if (name.equals(AndroidLogcatView.NO_FILTERS) || name.equals(AndroidLogcatView.EDIT_FILTER_CONFIGURATION)) { return new ValidationInfo(AndroidBundle.message("android.logcat.new.filter.dialog.name.busy.error", name)); } final AndroidConfiguredLogFilters.MyFilterEntry entry = AndroidConfiguredLogFilters.getInstance(myView.getProject()).findFilterEntryByName(name); if (entry != null && entry != mySelectedEntry) { return new ValidationInfo(AndroidBundle.message("android.logcat.new.filter.dialog.name.busy.error", name)); } ValidationInfo info = validatePattern( myTagField.getText().trim(), AndroidBundle.message("android.logcat.new.filter.dialog.incorrect.log.tag.pattern.error")); if (info != null) { return info; } info = validatePattern( myLogMessageField.getText().trim(), AndroidBundle.message("android.logcat.new.filter.dialog.incorrect.message.pattern.error")); if (info != null) { return info; } info = validatePattern( myPackageNameField.getText().trim(), AndroidBundle.message("android.logcat.new.filter.dialog.incorrect.application.name.pattern.error")); if (info != null) { return info; } boolean validPid = false; try { final String pidStr = myPidField.getText().trim(); final Integer pid = pidStr.length() > 0 ? Integer.parseInt(pidStr) : null; if (pid == null || pid.intValue() >= 0) { validPid = true; } } catch (NumberFormatException ignored) { } if (!validPid) { return new ValidationInfo(AndroidBundle.message("android.logcat.new.filter.dialog.incorrect.pid.error")); } return null; } private static ValidationInfo validatePattern(String pattern, String errorMessage) { try { if (!pattern.isEmpty()) { Pattern.compile(pattern, AndroidConfiguredLogFilters.getPatternCompileFlags(pattern)); } return null; } catch (PatternSyntaxException e) { String message = e.getMessage(); return new ValidationInfo(errorMessage + (message != null ? ('\n' + message) : "")); } } @Nullable public AndroidConfiguredLogFilters.MyFilterEntry getCustomLogFiltersEntry() { return mySelectedEntry; } private void updateConfiguredFilters() { myFiltersList.setEnabled(myFilterEntries.size() > 0); if (mySelectedEntry != null) { myFiltersList.setSelectedValue(mySelectedEntry.getName(), true); } else if (!myFilterEntries.isEmpty()) { myFiltersList.setSelectedIndex(0); } resetFieldEditors(); } private AndroidConfiguredLogFilters.MyFilterEntry createNewFilterEntry() { AndroidConfiguredLogFilters.MyFilterEntry entry = new AndroidConfiguredLogFilters.MyFilterEntry(); myFilterEntries.add(entry); entry.setName(getUniqueName()); return entry; } private String getUniqueName() { Set<String> names = new HashSet<String>(myFilterEntries.size()); for (AndroidConfiguredLogFilters.MyFilterEntry fe : myFilterEntries) { names.add(fe.getName()); } for (int i = 0; ; i++){ String n = NEW_FILTER_NAME_PREFIX + i; if (!names.contains(n)) { return n; } } } private class MyAddFilterAction extends AnAction { private MyAddFilterAction() { super(CommonBundle.message("button.add"), AndroidBundle.message("android.logcat.add.logcat.filter.button"), IconUtil.getAddIcon()); } @Override public void actionPerformed(AnActionEvent e) { mySelectedEntry = createNewFilterEntry(); myFiltersListModel.add(mySelectedEntry.getName()); updateConfiguredFilters(); } } private class MyRemoveFilterAction extends AnAction { private MyRemoveFilterAction() { super(CommonBundle.message("button.delete"), AndroidBundle.message("android.logcat.remove.logcat.filter.button"), IconUtil.getRemoveIcon()); } @Override public void update(AnActionEvent e) { super.update(e); e.getPresentation().setEnabled(getSelectedFilterName() != null); } @Override public void actionPerformed(AnActionEvent e) { int i = myFiltersList.getSelectedIndex(); // remove the entry from the model and from the displayed list myFilterEntries.remove(i); myFiltersListModel.remove(i); // select another entry mySelectedEntry = null; if (myFilterEntries.size() > 0) { if (i >= myFilterEntries.size()) { i = myFilterEntries.size() - 1; } mySelectedEntry = myFilterEntries.get(i); } updateConfiguredFilters(); } } private String getSelectedFilterName() { return (String)myFiltersList.getSelectedValue(); } }