blob: 4cd3d06b07a0de1c814185c02fdce0ac9845d009 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.netbeans.modules.gsf.testrunner.ui;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeListener;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.accessibility.AccessibleContext;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JPopupMenu;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
import org.openide.awt.ActionID;
import org.openide.awt.ActionRegistration;
import org.openide.awt.MouseUtils;
import org.openide.awt.TabbedPaneFactory;
import org.openide.util.Exceptions;
import org.openide.util.HelpCtx;
import org.openide.util.ImageUtilities;
import org.openide.util.NbBundle;
import org.openide.util.NbPreferences;
import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager;
import org.openide.windows.IOContainer;
import org.openide.windows.IOContainer.CallBacks;
import org.openide.windows.InputOutput;
/**
*
* @author Marian Petras, Erno Mononen
*/
public final class ResultWindow extends TopComponent {
/** unique ID of <code>TopComponent</code> (singleton) */
private static final String ID = "gsf-testrunner-results"; //NOI18N
/**
* instance/singleton of this class
*
* @see #getInstance
*/
private static WeakReference<ResultWindow> instance = null;
private Map<String,JSplitPane> viewMap = new HashMap<String,JSplitPane>();
private Map<String,InputOutput> ioMap = new HashMap<String,InputOutput>();
private static JTabbedPane tabPane;
private JPopupMenu pop;
private PopupListener popL;
private CloseListener closeL;
/**
* Returns a singleton of this class.
*
* @return singleton of this class
*/
public static ResultWindow getInstance() {
final ResultWindow[] result = new ResultWindow[1];
if (!SwingUtilities.isEventDispatchThread()) {
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
result[0] = getResultWindow();
}
});
} catch (InterruptedException ex) {
Exceptions.printStackTrace(ex);
} catch (InvocationTargetException ex) {
Exceptions.printStackTrace(ex);
}
} else {
result[0] = getResultWindow();
}
return result[0];
}
private static synchronized ResultWindow getResultWindow() {
ResultWindow result = (ResultWindow) WindowManager.getDefault().findTopComponent(ID);
if (result == null) {
result = getDefault();
}
return result;
}
/**
* Singleton accessor reserved for the window system only.
* The window system calls this method to create an instance of this
* <code>TopComponent</code> from a <code>.settings</code> file.
* <p>
* <em>This method should not be called anywhere except from the window
* system's code.</em>
*
* @return singleton - instance of this class
*/
public static synchronized ResultWindow getDefault() {
ResultWindow window = (instance != null) ? instance.get() : null;
if (window == null) {
window = new ResultWindow();
window.initActions();
instance = new WeakReference<ResultWindow>(window);
}
return window;
}
private void initActions() {
ActionMap actions = getActionMap();
actions.put("jumpNext", new PrevNextFailure(true)); //NOI18N
actions.put("jumpPrev", new PrevNextFailure(false)); //NOI18N
}
/** */
// private JSplitPane view;
// private Object lookup;
/** Creates a new instance of ResultWindow */
@NbBundle.Messages({"TITLE_TEST_RESULTS=Test Results",
"ACSN_TestResults=Test Results",
"ACSD_TestResults=Displays information about passed and failed tests and output generated by them"})
public ResultWindow() {
super();
setFocusable(true);
setLayout(new BorderLayout());
setName(ID);
setDisplayName(Bundle.TITLE_TEST_RESULTS());
setIcon(ImageUtilities.loadImage( "org/netbeans/modules/gsf/testrunner/ui/resources/testResults.png", true));//NOI18N
AccessibleContext accContext = getAccessibleContext();
accContext.setAccessibleName(Bundle.ACSN_TestResults());
accContext.setAccessibleDescription(Bundle.ACSD_TestResults());
pop = new JPopupMenu();
pop.add(new Close());
pop.add(new CloseAll());
pop.add(new CloseAllButCurrent());
popL = new PopupListener();
closeL = new CloseListener();
tabPane = TabbedPaneFactory.createCloseButtonTabbedPane();
tabPane.setMinimumSize(new Dimension(0, 0));
tabPane.addMouseListener(popL);
tabPane.addPropertyChangeListener(closeL);
add(tabPane);
}
/**
*/
public void addDisplayComponent(JSplitPane displayComp, InputOutput io) {
assert EventQueue.isDispatchThread();
String key = displayComp.getToolTipText();
boolean alwaysOpenNewTab = NbPreferences.forModule(StatisticsPanel.class).getBoolean(StatisticsPanel.PROP_ALWAYS_OPEN_NEW_TAB, false);
if (alwaysOpenNewTab) {
int frequency = getFrequency(viewMap.keySet(), key);
if (frequency > 0) {
key = key.concat(" #").concat(Integer.toString(frequency)); //NOI18N
displayComp.setToolTipText(key);
}
}
JSplitPane prevComp = viewMap.put(key, displayComp);
InputOutput prevIo = ioMap.put(key, io);
if (alwaysOpenNewTab || prevComp == null){
addView(displayComp);
}else{
replaceView(prevComp, displayComp);
if (prevIo != null) {
prevIo.closeInputOutput();
}
}
revalidate();
}
private int getFrequency(Set<String> c, String tooltip) {
int result = 0;
int max = 0;
for (String key : c) {
if (key.startsWith(tooltip)) {
result++;
int index = key.indexOf(" #"); //NOI18N
if (index != -1) {
max = Math.max(max, Integer.parseInt(key.substring(index + 2)));
}
}
}
return result == 0 ? 0 : Math.max(max + 1, result);
}
public void updateOptionStatus(String property, boolean selected) {
NbPreferences.forModule(StatisticsPanel.class).putBoolean(property, selected);
for (int i = 0; i < tabPane.getTabCount(); i++) {
StatisticsPanel sp = (StatisticsPanel)((JSplitPane)tabPane.getComponentAt(i)).getLeftComponent();
sp.updateOptionStatus(property, selected);
}
}
/**
*/
private void addView(final JSplitPane view) {
assert EventQueue.isDispatchThread();
view.setMinimumSize(new Dimension(0, 0));
tabPane.addTab(view.getToolTipText(), view);
tabPane.setSelectedComponent(view);
tabPane.validate();
}
private void replaceView(JSplitPane oldView, JSplitPane newView){
for (int i=0; i < tabPane.getTabCount(); i++){
if (oldView.equals(tabPane.getComponentAt(i))){
tabPane.setComponentAt(i, newView);
tabPane.setSelectedComponent(newView);
tabPane.validate();
copyFilterMask(oldView, newView);
continue;
}
}
}
private void copyFilterMask(JSplitPane oldView, JSplitPane newView) {
StatisticsPanel oldSP = (StatisticsPanel)oldView.getLeftComponent();
StatisticsPanel newSP = (StatisticsPanel)newView.getLeftComponent();
newSP.copyFilterMask(oldSP);
}
/**
*/
public void promote() {
assert EventQueue.isDispatchThread();
open();
requestVisible();
// don't activate, see #145382
//requestActive();
}
/**
* Sets the layout orientation of the contained result pane.
*
* @param orientation the orientation (see {@link JSplitPane#VERTICAL_SPLIT}
* and {@link JSplitPane#HORIZONTAL_SPLIT}) to set.
*/
public void setOrientation(int orientation) {
for(JSplitPane view: viewMap.values()){
if (view.getOrientation() != orientation) {
view.setOrientation(orientation);
}
}
}
/**
*/
@Override
protected String preferredID() {
return ID;
}
/**
*/
@Override
public HelpCtx getHelpCtx() {
return new HelpCtx(getClass());
}
/**
*/
@Override
public int getPersistenceType() {
return TopComponent.PERSISTENCE_ALWAYS;
}
/*
@Override
public Lookup getLookup() {
if (lookup == null) {
return super.getLookup();
}
if (lookup instanceof Reference) {
Object l = ((Reference) lookup).get();
if (l instanceof Lookup) {
return (Lookup) l;
}
}
return Lookup.EMPTY;
}
*/
/**
* Resolves to the {@linkplain #getDefault default instance} of this class.
*
* This method is necessary for correct functinality of window system's
* mechanism of persistence of top components.
*/
private Object readResolve() throws java.io.ObjectStreamException {
return ResultWindow.getDefault();
}
private boolean activated;
private JComponent outputComp;
private JComponent outputTab;
private IOContainer ioContainer;
public IOContainer getIOContainer() {
if (ioContainer == null) {
ioContainer = IOContainer.create(new IOContainerImpl());
}
return ioContainer;
}
public void setOutputComp(JComponent comp) {
outputComp = comp;
}
@Override
protected void componentActivated() {
activated = true;
}
@Override
protected void componentDeactivated() {
activated = false;
}
private static JSplitPane getCurrentResultView(){
if(tabPane == null) {
// Test Results Window is not opened yet
return null;
}
return (JSplitPane)tabPane.getSelectedComponent();
}
public @Override boolean requestFocusInWindow() {
JSplitPane view = getCurrentResultView();
if (view == null) {
return super.requestFocusInWindow();
}
Component left = view.getLeftComponent();
if (left == null) {
return super.requestFocusInWindow();
}
return left.requestFocusInWindow();
}
private void closeAll(boolean butCurrent) {
Component current = tabPane.getSelectedComponent();
Component[] c = tabPane.getComponents();
for (int i = 0; i< c.length; i++) {
if (butCurrent && c[i]==current) {
continue;
}
if(c[i] instanceof JSplitPane) {
removeView((JSplitPane) c[i]);
}
}
}
@Override
protected void componentClosed() {
closeAll(false);
super.componentClosed();
}
private void removeView(JSplitPane view) {
// probably it's need to stop testing if in progress?
if (view == null) {
view = (JSplitPane) tabPane.getSelectedComponent();
if (view == null) {
return;
}
}
tabPane.remove(view);
viewMap.remove(view.getToolTipText());
InputOutput io = ioMap.remove(view.getToolTipText());
if (io != null) {
io.closeInputOutput();
}
validate();
}
private class IOContainerImpl implements IOContainer.Provider {
public void remove(JComponent comp) {
outputTab = null;
outputComp.remove(comp);
}
public void select(JComponent comp) {
}
public JComponent getSelected() {
return outputTab;
}
public boolean isActivated() {
return activated;
}
public void open() {
}
public void requestActive() {
}
public void requestVisible() {
}
public void setIcon(JComponent comp, Icon icon) {
}
public void setTitle(JComponent comp, String name) {
}
public void setToolTipText(JComponent comp, String name) {
}
public void add(JComponent comp, CallBacks cb) {
outputTab = comp;
outputComp.add(comp);
}
public void setToolbarActions(JComponent comp, Action[] toolbarActions) {
}
public boolean isCloseable(JComponent comp) {
return false;
}
}
private final class PrevNextFailure extends AbstractAction {
private final boolean next;
public PrevNextFailure(boolean next) {
this.next = next;
}
public void actionPerformed(ActionEvent e) {
JSplitPane view = getCurrentResultView();
if (view == null || !(view.getLeftComponent() instanceof StatisticsPanel)) {
return;
}
StatisticsPanel statisticsPanel = (StatisticsPanel) view.getLeftComponent();
if (next) {
statisticsPanel.selectNextFailure();
} else {
statisticsPanel.selectPreviousFailure();
}
}
}
@ActionID(category = "CommonTestRunner", id = "org.netbeans.modules.gsf.testrunner.ui.ResultWindow.Rerun")
@ActionRegistration(displayName = "#CTL_Rerun")
@NbBundle.Messages("CTL_Rerun=Rerun All Tests")
public static final class Rerun extends AbstractAction {
@Override
public void actionPerformed(ActionEvent e) {
StatisticsPanel statisticsPanel = getStatisticsPanel();
if(statisticsPanel != null) {
statisticsPanel.rerun(false);
}
}
}
@ActionID(category = "CommonTestRunner", id = "org.netbeans.modules.gsf.testrunner.ui.ResultWindow.RerunFailed")
@ActionRegistration(displayName = "#CTL_RerunFailed")
@NbBundle.Messages("CTL_RerunFailed=Rerun Failed Tests")
public static final class RerunFailed extends AbstractAction {
@Override
public void actionPerformed(ActionEvent e) {
StatisticsPanel statisticsPanel = getStatisticsPanel();
if(statisticsPanel != null) {
statisticsPanel.rerun(true);
}
}
}
private static StatisticsPanel getStatisticsPanel() {
JSplitPane view = getCurrentResultView();
if (view == null || !(view.getLeftComponent() instanceof StatisticsPanel)) {
return null;
}
return (StatisticsPanel) view.getLeftComponent();
}
@NbBundle.Messages("LBL_CloseWindow=Close Tab")
private class Close extends AbstractAction {
public Close() {
super(Bundle.LBL_CloseWindow());
}
@Override
public void actionPerformed(ActionEvent e) {
removeView(null);
}
}
@NbBundle.Messages("LBL_CloseAll=Close All Tabs")
private final class CloseAll extends AbstractAction {
public CloseAll() {
super(Bundle.LBL_CloseAll());
}
@Override
public void actionPerformed(ActionEvent e) {
closeAll(false);
}
}
@NbBundle.Messages("LBL_CloseAllButCurrent=Close Other Tabs")
private class CloseAllButCurrent extends AbstractAction {
public CloseAllButCurrent() {
super(Bundle.LBL_CloseAllButCurrent());
}
public void actionPerformed(ActionEvent e) {
closeAll(true);
}
}
private class CloseListener implements PropertyChangeListener {
public void propertyChange(java.beans.PropertyChangeEvent evt) {
if (TabbedPaneFactory.PROP_CLOSE.equals(evt.getPropertyName())) {
removeView((JSplitPane) evt.getNewValue());
}
}
}
private class PopupListener extends MouseUtils.PopupMouseAdapter {
protected void showPopup (MouseEvent e) {
pop.show(ResultWindow.this, e.getX(), e.getY());
}
}
}