/*
* Copyright 2003-2014 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 jetbrains.mps.ide.messages;
import com.intellij.openapi.editor.colors.EditorColors;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import jetbrains.mps.ide.search.AbstractSearchPanel;
import jetbrains.mps.ide.search.SearchHistoryStorage;
import javax.swing.JList;
import javax.swing.ListCellRenderer;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import java.awt.Color;
import java.awt.Component;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
class MessageToolSearchPanel extends AbstractSearchPanel {
private final JList myList;
private final SearchHistoryStorage myHistory;
private int myCountResult = 0;
private final List<Integer> myResults = new ArrayList<>();
private final MyCellRenderer myRenderer = new MyCellRenderer();
private ListCellRenderer myOriginalCellRenderer;
public MessageToolSearchPanel(JList list, SearchHistoryStorage history) {
myList = list;
myHistory = history;
// Update search if list of messages is changed in any way
myList.getModel().addListDataListener(new ListDataListener() {
@Override
public void intervalAdded(ListDataEvent e) {
reSearch();
}
@Override
public void intervalRemoved(ListDataEvent e) {
reSearch();
}
@Override
public void contentsChanged(ListDataEvent e) {
reSearch();
}
private void reSearch() {
myRenderer.search();
}
});
}
@Override
public void goToPrevious() {
if (myResults.isEmpty()) {
return;
}
addToHistory();
int selected = myList.getSelectedIndex();
if (selected != -1) {
for (int i = myResults.size() - 1; i >= 0; i--) {
if (selected > myResults.get(i)) {
selectAndReveal(myResults.get(i));
return;
}
}
}
selectAndReveal(myResults.get(myResults.size() - 1));
}
@Override
public void goToNext() {
if (myResults.isEmpty()) {
return;
}
addToHistory();
int selected = myList.getSelectedIndex();
if (selected != -1) {
for (Integer index : myResults) {
if (selected < index) {
selectAndReveal(index);
return;
}
}
}
selectAndReveal(myResults.get(0));
}
@Override
protected SearchHistoryStorage getSearchHistory() {
return myHistory;
}
@Override
protected void search() {
myRenderer.search();
}
@Override
public void activate() {
myOriginalCellRenderer = myList.getCellRenderer();
myList.setCellRenderer(myRenderer);
super.activate();
}
@Override
protected void deactivate() {
setVisible(false);
myFindResult.setText("");
myText.setText("");
revalidate();
myList.setCellRenderer(myOriginalCellRenderer);
myList.requestFocus();
}
private void selectAndReveal(int index) {
myList.setSelectedIndex(index);
myList.ensureIndexIsVisible(index);
}
private class MyCellRenderer extends MessagesListCellRenderer {
private int myIndex = -1;
private final List<Integer> myColumnResults = new ArrayList<>();
private void updateView() {
updateSearchReport(myCountResult);
}
public void search() {
myCountResult = 0;
myResults.clear();
myColumnResults.clear();
for (int i = 0; i < myList.getModel().getSize(); i++) {
StringBuilder line = new StringBuilder();
line.append(myList.getModel().getElementAt(i));
if (myText.getText().length() != 0) {
Pattern pattern = getPattern();
if (pattern == null) {
setErrorMessage("Incorrect regular expression");
return;
}
setErrorMessage(null);
Matcher matcher = pattern.matcher(line.toString());
while (matcher.find()) {
int column = matcher.start() + 11;
if (!(myColumnResults.contains(column) && myResults.contains(i)
&& myColumnResults.indexOf(column) == myResults.indexOf(i))) {
myColumnResults.add(column);
myResults.add(i);
myCountResult++;
}
}
}
}
updateView();
myList.repaint();
}
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
myIndex = index;
return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
}
@Override
public void paint(Graphics g) {
super.paint(g);
if (myText.getText().length() == 0) {
myFindResult.setText("");
}
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (myResults.contains(myIndex)) {
final Color highlightColor =
EditorColorsManager.getInstance().getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES).getBackgroundColor();
List<Integer> columns = new ArrayList<>();
for (int i = 0; i < myResults.size(); i++) {
int value = myResults.get(i);
if (value == myIndex) {
columns.add(myColumnResults.get(i));
}
}
for (Integer column : columns) {
final int endIndex = column + myText.getText().length();
if (getText().length() <= endIndex) {
continue; // just skip this outdated result to avoid IndexOutOfBoundsException (prevent painting problems).
}
final String foundText = getText().substring(column, endIndex);
Graphics2D g2 = (Graphics2D) g;
FontMetrics fontMetrics = g2.getFontMetrics();
Color color = g2.getColor();
g2.setColor(highlightColor);
int startTextX = getInsets().left + getIcon().getIconWidth()
+ getIconTextGap()
+ fontMetrics.stringWidth(getText().substring(0, column));
g2.fillRect(startTextX, 1,
fontMetrics.stringWidth(foundText),
fontMetrics.getHeight() - 1);
g2.setColor(color);
g2.drawString(foundText, startTextX, fontMetrics.getHeight() - fontMetrics.getLeading() - 1);
}
}
}
}
}