/* * Lilith - a log event viewer. * Copyright (C) 2007-2017 Joern Huxhorn * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package de.huxhorn.lilith.swing; import de.huxhorn.lilith.data.eventsource.EventWrapper; import de.huxhorn.lilith.engine.EventSource; import de.huxhorn.lilith.swing.preferences.SavedCondition; import de.huxhorn.sulky.buffers.Buffer; import de.huxhorn.sulky.buffers.Dispose; import de.huxhorn.sulky.conditions.Condition; import java.awt.BorderLayout; import java.awt.CardLayout; import java.awt.Component; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.DefaultComboBoxModel; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.ListCellRenderer; import javax.swing.SwingConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public abstract class ComboPaneViewContainer<T extends Serializable> extends ViewContainer<T> { private static final long serialVersionUID = -399179541035021703L; private static final String UNFILTERED = "Unfiltered"; private final Logger logger = LoggerFactory.getLogger(ComboPaneViewContainer.class); private SourceChangeListener sourceChangeListener; private boolean disposed; private EventWrapper<T> selectedEvent; private JPanel contentPane; private final JComboBox<ViewHolder> comboBox; private DefaultComboBoxModel<ViewHolder> comboBoxModel; private int comboCounter; private CardLayout cardLayout; private CloseAction closeAction; private JPanel comboBoxPane; ComboPaneViewContainer(MainFrame mainFrame, EventSource<T> eventSource) { super(mainFrame, eventSource); disposed = false; comboBoxPane = new JPanel(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); comboBoxModel = new DefaultComboBoxModel<>(); comboBox = new JComboBox<>(comboBoxModel); comboBox.setRenderer(new MyComboBoxRenderer()); comboBox.setEditable(false); comboBox.addItemListener(new ComboItemListener()); gbc.gridx = 0; gbc.gridy = 0; gbc.weightx = 0.0; gbc.weighty = 0.0; closeAction = new CloseAction(); JButton closeButton = new JButton(closeAction); closeButton.setMargin(new Insets(0, 0, 0, 0)); comboBoxPane.add(closeButton, gbc); gbc.gridx = 1; gbc.gridy = 0; gbc.weightx = 1.0; gbc.weighty = 1.0; gbc.fill = GridBagConstraints.BOTH; comboBoxPane.add(comboBox, gbc); cardLayout = new CardLayout(); contentPane = new JPanel(cardLayout); setLayout(new BorderLayout()); add(comboBoxPane, BorderLayout.NORTH); add(contentPane, BorderLayout.CENTER); sourceChangeListener = new SourceChangeListener(); addView(getDefaultView()); } private class CloseAction extends AbstractAction { private static final long serialVersionUID = 7687142682378711767L; private CloseAction() { super(); putValue(Action.SMALL_ICON, Icons.CLOSE_16_ICON); putValue(Action.SHORT_DESCRIPTION, "Close filtered view."); } public void actionPerformed(ActionEvent e) { closeCurrentFilter(); } } private class ComboItemListener implements ItemListener { public void itemStateChanged(ItemEvent e) { ViewHolder holder = getSelectedItem(); if(holder == null) { return; } Condition filter = null; EventWrapperViewPanel<T> view = holder.getView(); if(view != null) { EventSource source = view.getEventSource(); filter = source.getFilter(); } if(filter == null) { comboBox.setToolTipText(UNFILTERED); } else { comboBox.setToolTipText(TextPreprocessor.preformattedTooltip(TextPreprocessor.cropTextBlock(TextPreprocessor.formatCondition(filter)))); } cardLayout.show(contentPane, holder.getId()); if(getViewIndex() > 0) { closeAction.setEnabled(true); } else { closeAction.setEnabled(false); } selectedViewChanged(); } } /* * Only used for non-generic identity check in ViewHolder.equals() */ private interface ViewHolderIdentity { String getId(); } private class ViewHolder implements ViewHolderIdentity { private final EventWrapperViewPanel<T> view; private final String id; private ViewHolder(EventWrapperViewPanel<T> view) { this.view = view; comboCounter++; this.id = "" + comboCounter; } public EventWrapperViewPanel<T> getView() { return view; } public String getId() { return id; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || !(o instanceof ViewHolderIdentity)) return false; ViewHolderIdentity that = (ViewHolderIdentity) o; return id.equals(that.getId()); } @Override public int hashCode() { return id.hashCode(); } public String toString() { return "ViewHolder[id=" + id + ", view=" + view + "]"; } } public void addView(EventWrapperViewPanel<T> view) { EventSource source = view.getEventSource(); if(logger.isInfoEnabled()) logger.info("Adding view for {}", source); ViewHolder holder = new ViewHolder(view); Condition filter = source.getFilter(); if(filter == null) { comboBoxModel.insertElementAt(holder, 0); } else { comboBoxModel.addElement(holder); } comboBoxModel.setSelectedItem(holder); contentPane.add(holder.getView(), holder.getId()); cardLayout.show(contentPane, holder.getId()); view.addPropertyChangeListener(sourceChangeListener); view.requestFocusInWindow(); selectedViewChanged(); } public void updateViews() { if(comboBoxPane.isVisible()) { comboBoxPane.repaint(); } for(int i=0;i<comboBoxModel.getSize();i++) { ViewHolder holder = comboBoxModel.getElementAt(i); if(holder == null) { continue; } EventWrapperViewPanel<T> view = holder.getView(); if(view == null) { continue; } view.updateView(); } contentPane.repaint(); } public void updateViewScale(double scale) { for(int i = 0; i < comboBoxModel.getSize(); i++) { ViewHolder holder = comboBoxModel.getElementAt(i); if(holder == null) { continue; } EventWrapperViewPanel<T> view = holder.getView(); if(view == null) { continue; } view.setScaleFactor(scale); } } public void removeView(EventWrapperViewPanel<T> view, boolean dispose) { ViewHolder found = null; for(int i = 0; i < comboBoxModel.getSize(); i++) { ViewHolder holder = comboBoxModel.getElementAt(i); if(holder == null) { continue; } EventWrapperViewPanel<T> current = holder.getView(); if(current == view) { found = holder; break; } } if(found != null) { comboBoxModel.removeElement(found); contentPane.remove(found.getView()); view.removePropertyChangeListener(sourceChangeListener); if(logger.isDebugEnabled()) logger.debug("Removed view {}.", view); if(dispose) { view.dispose(); Buffer<EventWrapper<T>> buffer = view.getEventSource().getBuffer(); Dispose.dispose(buffer); if(logger.isDebugEnabled()) logger.debug("Disposed view {}.", view); } selectedViewChanged(); } } public void showDefaultView() { if(comboBoxModel.getSize() > 0) { ViewHolder holder = comboBoxModel.getElementAt(0); if(holder != null) { comboBoxModel.setSelectedItem(holder); cardLayout.show(contentPane, holder.getId()); selectedViewChanged(); } } } public void addNotify() { super.addNotify(); if(logger.isDebugEnabled()) logger.debug("addNotify - parent: {}", getParent()); } public void scrollToEvent() { EventWrapperViewPanel<T> selectedView = getSelectedView(); if(selectedView != null) { selectedView.scrollToEvent(); setSelectedEvent(selectedView.getSelectedEvent()); } } public void removeNotify() { super.removeNotify(); if(logger.isDebugEnabled()) logger.debug("removeNotify"); } private void selectedViewChanged() { EventWrapperViewPanel<T> selectedView = getSelectedView(); if(selectedView != null) { selectedView.scrollToEvent(); setSelectedEvent(selectedView.getSelectedEvent()); } else { setSelectedEvent(null); } int count = comboBoxModel.getSize(); if(count > 1) { comboBoxPane.setVisible(true); } else { comboBoxPane.setVisible(false); } if(selectedView != null) { selectedView.focusTable(); } fireChange(); } public void dispose() { super.dispose(); disposed = true; List<ViewHolder> removedPanes = new ArrayList<>(); for(int i = 0; i < comboBoxModel.getSize(); i++) { removedPanes.add(comboBoxModel.getElementAt(i)); } for(ViewHolder current : removedPanes) { removeView(current.getView(), true); } fireChange(); } public boolean isDisposed() { return disposed; } private void setSelectedEvent(EventWrapper<T> selectedEvent) { Object oldValue = this.selectedEvent; this.selectedEvent = selectedEvent; Object newValue = this.selectedEvent; firePropertyChange(SELECTED_EVENT_PROPERTY_NAME, oldValue, newValue); } public EventWrapper<T> getSelectedEvent() { return selectedEvent; } public EventWrapperViewPanel<T> getViewAt(int index) { if(index >= 0 && index < comboBoxModel.getSize()) { ViewHolder current = comboBoxModel.getElementAt(index); if(current != null) { return current.getView(); } } return null; } public EventWrapperViewPanel<T> getSelectedView() { ViewHolder holder = getSelectedItem(); if(holder != null) { return holder.getView(); } return null; } public void setViewIndex(int index) { ViewHolder holder = comboBoxModel.getElementAt(index); comboBoxModel.setSelectedItem(holder); selectedViewChanged(); } public int getViewIndex() { return comboBoxModel.getIndexOf(comboBoxModel.getSelectedItem()); } public int getViewCount() { return comboBoxModel.getSize(); } /* * What the I don't even... * set/getSelectedItem, Y U NO T?? */ private ViewHolder getSelectedItem() { Object item = comboBoxModel.getSelectedItem(); if(item == null) { return null; } int index = comboBoxModel.getIndexOf(item); if(index < 0) { return null; } return comboBoxModel.getElementAt(index); } public void closeCurrentFilter() { ViewHolder holder = getSelectedItem(); if(holder == null) { return; } int index = comboBoxModel.getIndexOf(holder); if(index > 0) { EventWrapperViewPanel<T> lvp = holder.getView(); removeView(lvp, true); selectedViewChanged(); } } public void closeOtherFilters() { ViewHolder holder = getSelectedItem(); int index = 0; if(holder != null) { index = comboBoxModel.getIndexOf(holder); } int tabCount = comboBoxModel.getSize(); List<ViewHolder> removedPanes = new ArrayList<>(); for(int i = 1; i < tabCount; i++) { if(i != index) { ViewHolder current = comboBoxModel.getElementAt(i); if(current == null) { continue; } removedPanes.add(current); } } for(ViewHolder current : removedPanes) { removeView(current.getView(), true); } selectedViewChanged(); } public void closeAllFilters() { List<ViewHolder> removedPanes = new ArrayList<>(); for(int i = 1; i < comboBoxModel.getSize(); i++) { ViewHolder current = comboBoxModel.getElementAt(i); if(current == null) { continue; } removedPanes.add(current); } for(ViewHolder current : removedPanes) { removeView(current.getView(), true); } selectedViewChanged(); } public void setShowingStatusBar(boolean showingStatusBar) { int tabCount = comboBoxModel.getSize(); for(int i = 0; i < tabCount; i++) { ViewHolder current = comboBoxModel.getElementAt(i); if(current == null) { continue; } EventWrapperViewPanel<T> view = current.getView(); if(view == null) { continue; } view.setShowingStatusBar(showingStatusBar); } } private class SourceChangeListener implements PropertyChangeListener { @SuppressWarnings({"unchecked"}) public void propertyChange(PropertyChangeEvent evt) { String propName = evt.getPropertyName(); switch (propName) { case EventWrapperViewPanel.EVENT_SOURCE_PROPERTY: if (logger.isDebugEnabled()) logger.debug("EventSource changed: {}", evt.getNewValue()); EventWrapperViewPanel<T> lvp = (EventWrapperViewPanel<T>) evt.getSource(); removeView(lvp, false); addView(lvp); break; case EventWrapperViewPanel.SELECTED_EVENT_PROPERTY: if (getSelectedView() == evt.getSource()) { if (logger.isDebugEnabled()) logger.debug("EventSource changed: {}", evt.getNewValue()); setSelectedEvent((EventWrapper<T>) evt.getNewValue()); } break; default: if (logger.isDebugEnabled()) logger.debug("Other change: {}", propName); fireChange(); break; } } } private class MyComboBoxRenderer implements ListCellRenderer<ViewHolder> { private JLabel label; MyComboBoxRenderer() { label = new JLabel(); label.setOpaque(true); label.setHorizontalAlignment(SwingConstants.LEFT); label.setVerticalAlignment(SwingConstants.CENTER); } @Override public Component getListCellRendererComponent(JList<? extends ViewHolder> list, ViewHolder value, int index, boolean isSelected, boolean cellHasFocus) { //noinspection Duplicates if(isSelected) { label.setBackground(list.getSelectionBackground()); label.setForeground(list.getSelectionForeground()); } else { label.setBackground(list.getBackground()); label.setForeground(list.getForeground()); } String title = null; String toolTip = null; if(value != null) { EventWrapperViewPanel<T> view = value.getView(); if (view != null) { EventSource source = view.getEventSource(); Condition filter = source.getFilter(); if (filter == null) { title = UNFILTERED; toolTip = title; } else { SavedCondition savedCondition = getMainFrame().getApplicationPreferences().resolveSavedCondition(filter); if (savedCondition != null) { title = savedCondition.getName(); } else { title = filter.toString(); } toolTip = TextPreprocessor.preformattedTooltip(TextPreprocessor.cropTextBlock(TextPreprocessor.formatCondition(filter))); } } } label.setText(title); label.setToolTipText(toolTip); return label; } } }