org.parosproxy.paros.view.WorkbenchPanel Maven / Gradle / Ivy
Show all versions of zap Show documentation
/*
*
* Paros and its related class files.
*
* Paros is an HTTP/HTTPS proxy for assessing web application security.
* Copyright (C) 2003-2004 Chinotec Technologies Company
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the Clarified Artistic License
* as published by the Free Software Foundation.
*
* 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
* Clarified Artistic License for more details.
*
* You should have received a copy of the Clarified Artistic License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// ZAP: 2011/05/31 Added option to dynamically change the display
// ZAP: 2011/07/25 Added automatically save/restore of divider locations
// ZAP: 2013/02/17 Issue 496: Allow to see the request and response at the same
// time in the main window
// ZAP: 2013/02/26 Issue 540: Maximised work tabs hidden when response tab
// position changed
// ZAP: 2013/03/03 Issue 546: Remove all template Javadoc comments
// ZAP: 2013/05/02 Removed redundant final modifiers from private methods
// ZAP: 2013/12/13 Added support for 'Full Layout'.
// ZAP: 2014/01/28 Issue 207: Support keyboard shortcuts
// ZAP: 2014/10/07 Issue 1357: Hide unused tabs
// ZAP: 2015/02/11 Ensure that a tab is always selected when the layout is switched
// ZAP: 2015/12/14 Disable request/response tab buttons location when in full layout
// ZAP: 2016/04/06 Fix layouts' issues
// ZAP: 2017/08/30 Add tool tip to response tab.
// ZAP: 2018/02/14 Add new layout ResponsePanelPosition.TAB_SIDE_BY_SIDE (Issue 4331).
// ZAP: 2018/04/03 Update for behavioural changes in TabbedPanel2.
// ZAP: 2019/06/01 Normalise line endings.
// ZAP: 2019/06/05 Normalise format/style.
// ZAP: 2020/11/02 Add OneTouchExapandable control to Sites Tree/Request&Response panels
// ZAP: 2020/11/26 Use Log4j 2 classes for logging.
// ZAP: 2022/02/26 Remove deprecated methods in 2.5.0
// ZAP: 2022/09/21 Use format specifiers instead of concatenation when logging.
// ZAP: 2023/01/10 Tidy up logger.
package org.parosproxy.paros.view;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.parosproxy.paros.Constant;
import org.parosproxy.paros.extension.AbstractPanel;
import org.parosproxy.paros.extension.option.OptionsParamView;
import org.zaproxy.zap.utils.DisplayUtils;
import org.zaproxy.zap.view.ComponentMaximiser;
import org.zaproxy.zap.view.ComponentMaximiserMouseListener;
import org.zaproxy.zap.view.TabbedPanel2;
/**
* A workbench panel, responsible to show and manage the main panels.
*
* @since 1.0.0
*/
@SuppressWarnings("serial")
public class WorkbenchPanel extends JPanel {
/**
* The layouts available to a {@code WorkbenchPanel}.
*
* @since 2.5.0
* @see ResponsePanelPosition
*/
public enum Layout {
/**
* Divides the panel in 3 areas, {@code select}, {@code status} and {@code work}, containing
* the {@link WorkbenchPanel.PanelType#SELECT SELECT}, {@link
* WorkbenchPanel.PanelType#STATUS STATUS} and {@link WorkbenchPanel.PanelType#WORK WORK}
* panels, respectively. The {@code select} area occupies the left portion of the workbench
* panel while the areas {@code work} and {@code status} occupy the top-right and
* bottom-right portions of the workbench panel, respectively.
*
* All the areas can be resized (using {@code JSplitPane}s) and maximised (using {@link
* ComponentMaximiser}).
*/
EXPAND_SELECT(0),
/**
* The default layout, divides the panel in 3 areas, {@code select}, {@code status} and
* {@code work}, containing the {@link WorkbenchPanel.PanelType#SELECT SELECT}, {@link
* WorkbenchPanel.PanelType#STATUS STATUS} and {@link WorkbenchPanel.PanelType#WORK WORK}
* panels, respectively. The {@code status} area is located at the bottom of the workbench
* panel while the areas {@code select} and {@code work} are located in the top-left and
* top-right portions of the workbench panel, respectively.
*
*
All the areas can be resized (using {@code JSplitPane}s) and maximised (using {@link
* ComponentMaximiser}).
*/
EXPAND_STATUS(1),
/**
* A layout that has only one area that occupies the whole workbench panel and where all
* panels ( {@link WorkbenchPanel.PanelType#SELECT SELECT}, {@link
* WorkbenchPanel.PanelType#STATUS STATUS} and {@link WorkbenchPanel.PanelType#WORK WORK})
* are shown.
*
*
Since it already occupies the whole workbench panel the area can not be resized nor
* maximised.
*/
FULL(2);
/** The ID of the layout. */
private final int id;
private Layout(int id) {
this.id = id;
}
/**
* Gets the ID of the layout.
*
*
Unique among all layouts, thus suitable for persistence of options.
*
* @return the ID of the layout
* @see #getLayout(int)
*/
public int getId() {
return id;
}
/**
* Gets the {@code Layout} corresponding to the given ID.
*
* @param id the ID of the layout
* @return the {@code Layout} corresponding to the given ID, or {@link #EXPAND_STATUS} if
* the ID is unknown.
*/
public static Layout getLayout(int id) {
if (id == Layout.EXPAND_SELECT.getId()) {
return Layout.EXPAND_SELECT;
}
if (id == Layout.EXPAND_STATUS.getId()) {
return Layout.EXPAND_STATUS;
}
if (id == Layout.FULL.getId()) {
return Layout.FULL;
}
return Layout.EXPAND_STATUS;
}
}
/**
* The position of the response panel, with respect to the request panel.
*
*
The position of the response panel can be changed dynamically but might not be used by all
* layouts, for example, the Full Layout.
*
* @since 2.5.0
* @see Layout
*/
public enum ResponsePanelPosition {
/** Request and response panels are shown, side-by-side, in the same tabbed panel. */
TABS_SIDE_BY_SIDE,
/**
* Request panel is shown above the response panel, in different tabbed panels.
*
*
Not supported in {@link Layout#FULL FULL layout}.
*/
PANEL_ABOVE,
/**
* Request and response panels are shown, side-by-side, in different tabbed panels.
*
*
Not supported in {@link Layout#FULL FULL layout}.
*/
PANELS_SIDE_BY_SIDE,
/**
* Request and response panels are shown, side-by-side, in the same tab.
*
* @since 2.8.0
*/
TAB_SIDE_BY_SIDE
}
/**
* The type of panels added to the {@code WorkbenchPanel}, used as hint when doing the layout of
* the panels.
*
*
Panels of the same type might be shown in the same area of the workbench panel. Some
* layouts, like {@link Layout#FULL}, might choose to ignore the type of the panels.
*
* @since 2.5.0
*/
public enum PanelType {
/**
* The panels that provide status/data about extensions/components, for example, History
* panel.
*/
STATUS,
/**
* The panels that allow to select/display general data, for example, Sites or Scripts
* panels.
*/
SELECT,
/**
* The panels that allow to display/manipulate common data, for example, Request and
* Response panels.
*/
WORK
}
private static final long serialVersionUID = -4610792807151921550L;
private static final Logger LOGGER = LogManager.getLogger(WorkbenchPanel.class);
private static final String PREF_DIVIDER_LOCATION = "divider.location";
private static final String DIVIDER_VERTICAL = "vertical";
private static final String DIVIDER_HORIZONTAL = "horizontal";
private final Preferences preferences;
private final String prefnzPrefix = this.getClass().getSimpleName() + ".";
/**
* The request panel, used for positioning the response panel.
*
* @see #setResponsePanelPosition(ResponsePanelPosition)
*/
private final AbstractPanel requestPanel;
/**
* The response panel, moved around depending on the option {@code response panel position}.
*
* @see #setResponsePanelPosition(ResponsePanelPosition)
*/
private final AbstractPanel responsePanel;
/**
* A tabbed panel to show the response panel, when it is shown separately from other panels.
*
*
Main purpose is to show the request and response panels at the same time (in different
* tabs).
*
* @see #setResponsePanelPosition(ResponsePanelPosition)
*/
private final TabbedPanel2 responseTabbedPanel;
/**
* The index of the {@link #requestPanel} in the tabbed pane of the current {@link #layout}.
*
* @see #getCurrentRequestTabIndex()
*/
private int requestTabIndex;
/**
* A {@code JSplitPane} to show the request and response in the same tab.
*
*
Lazily initialised.
*
* @see ResponsePanelPosition#TAB_SIDE_BY_SIDE
* @see #splitResponsePanelWithRequestPanel(int, TabbedPanel2)
*/
private JSplitPane splitRequestAndResponse;
/**
* The panel that shows the request and response in the same tab.
*
*
Lazily initialised.
*
* @see ResponsePanelPosition#TAB_SIDE_BY_SIDE
* @see #splitResponsePanelWithRequestPanel(int, TabbedPanel2)
*/
private AbstractPanel splitRequestAndResponsePanel;
/**
* The object to maximise the components when in some layouts.
*
*
The components are not maximised in Full Layout, they already occupy the whole workbench
* panel.
*/
private final ComponentMaximiser componentMaximiser;
/**
* The layout of the workbench panel.
*
* @see #setWorkbenchLayout(Layout)
*/
private Layout layout;
/**
* The position of the response panel.
*
* @see #setResponsePanelPosition(ResponsePanelPosition)
*/
private ResponsePanelPosition responsePanelPosition;
/**
* The tabbed panel for {@link PanelType#STATUS STATUS} panels.
*
*
Lazily initialised.
*
* @see #getTabbedStatus()
*/
private TabbedPanel2 tabbedStatus;
/**
* The {@code JPanel} that has the tabbed panel {@link #tabbedStatus}, represents the {@code
* status} area.
*
*
Lazily initialised.
*
* @see #getPaneStatus()
*/
private JPanel paneStatus;
/**
* The tabbed panel for {@link PanelType#SELECT SELECT} panels.
*
*
Lazily initialised.
*
* @see #getTabbedSelect()
*/
private TabbedPanel2 tabbedSelect;
/**
* The {@code JPanel} that has the tabbed panel {@link #tabbedSelect}, represents the {@code
* select} area.
*
*
Lazily initialised.
*
* @see #getPaneSelect()
*/
private JPanel paneSelect;
/**
* The tabbed panel for {@link PanelType#WORK WORK} panels.
*
*
Lazily initialised.
*
* @see #getTabbedWork()
*/
private TabbedPanel2 tabbedWork;
/**
* The {@code JPanel} that has the tabbed panel {@link #tabbedWork}, represents the {@code work}
* area.
*
*
Lazily initialised.
*
* @see #getPaneWork()
*/
private JPanel paneWork;
/**
* The tabbed panel for all {@link PanelType types} of panels, when in {@link Layout#FULL FULL}
* layout.
*
*
Lazily initialised.
*
* @see #getTabbedFull()
*/
private TabbedPanel2 tabbedFull;
/**
* The {@code JPanel} that has the tabbed panel {@link #tabbedFull}, that is, the {@link
* Layout#FULL FULL} layout.
*
*
Lazily initialised.
*
* @see #getFullLayoutPanel()
*/
private JPanel fullLayoutPanel;
/**
* Flag that indicates whether or not the tabs should show the panels' names.
*
*
When false only the icons are shown, otherwise it is shown the icons and the names.
*/
private boolean showTabNames;
/**
* Constructs a {@code WorkbenchPanel} with the given options and request and response panels.
*
* @param viewOptions the options
* @param requestPanel the request panel
* @param responsePanel the response panel
* @throws IllegalArgumentException if any of the parameters is {@code null}.
* @since 2.5.0
*/
public WorkbenchPanel(
OptionsParamView viewOptions, AbstractPanel requestPanel, AbstractPanel responsePanel) {
super(new BorderLayout());
validateNotNull(viewOptions, "viewOptions");
validateNotNull(requestPanel, "requestPanel");
validateNotNull(responsePanel, "responsePanel");
this.requestPanel = requestPanel;
this.responsePanel = responsePanel;
this.componentMaximiser = new ComponentMaximiser(this);
this.showTabNames = true;
ComponentMaximiserMouseListener maximiseMouseListener =
new ComponentMaximiserMouseListener(viewOptions, componentMaximiser);
responseTabbedPanel = new TabbedPanel2();
responseTabbedPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
responseTabbedPanel.addMouseListener(maximiseMouseListener);
getTabbedWork().addMouseListener(maximiseMouseListener);
getTabbedStatus().addMouseListener(maximiseMouseListener);
getTabbedSelect().addMouseListener(maximiseMouseListener);
addPanel(requestPanel, PanelType.WORK);
addPanel(responsePanel, PanelType.WORK);
this.preferences = Preferences.userNodeForPackage(getClass());
requestTabIndex = -1;
setResponsePanelPosition(ResponsePanelPosition.TABS_SIDE_BY_SIDE);
setWorkbenchLayout(Layout.EXPAND_STATUS);
}
/**
* Validates that the given {@code parameter} is not {@code null}, throwing an {@code
* IllegalArgumentException} if it is.
*
*
The given parameter name is used in the exception message to indicate which parameter must
* not be {@code null}.
*
* @param parameter the parameter to be validated
* @param parameterName the name of the parameter
* @throws IllegalArgumentException if the parameter is {@code null}.
*/
private static void validateNotNull(Object parameter, String parameterName) {
if (parameter == null) {
throw new IllegalArgumentException("Parameter " + parameterName + " must not be null");
}
}
/**
* Gets the layout of the workbench panel.
*
* @return the layout, never {@code null}
* @since 2.5.0
*/
public Layout getWorkbenchLayout() {
return layout;
}
/**
* Sets the layout of the workbench panel.
*
* @param layout the layout to set
* @throws IllegalArgumentException if the given parameter is {@code null}.
* @since 2.5.0
*/
void setWorkbenchLayout(Layout layout) {
validateNotNull(layout, "layout");
if (this.layout == layout) {
return;
}
requestTabIndex = getCurrentRequestTabIndex();
Layout previousLayout = this.layout;
this.layout = layout;
componentMaximiser.unmaximiseComponent();
removeAll();
List visiblePanels;
switch (layout) {
case FULL:
visiblePanels = getTabbedStatus().getVisiblePanels();
getTabbedStatus().hideAllTabs();
visiblePanels.addAll(getTabbedWork().getVisiblePanels());
getTabbedWork().hideAllTabs();
visiblePanels.addAll(getTabbedSelect().getVisiblePanels());
getTabbedSelect().hideAllTabs();
getTabbedFull().setVisiblePanels(visiblePanels);
updateFullLayout();
this.add(getFullLayoutPanel());
break;
case EXPAND_SELECT:
case EXPAND_STATUS:
default:
this.add(
layout == Layout.EXPAND_STATUS
? createStatusPanelsSplit()
: createSelectPanelsSplit());
if (previousLayout == Layout.FULL) {
visiblePanels = getTabbedFull().getVisiblePanels();
getTabbedFull().hideAllTabs();
getTabbedStatus().setVisiblePanels(visiblePanels);
getTabbedWork().setVisiblePanels(visiblePanels);
getTabbedSelect().setVisiblePanels(visiblePanels);
setResponsePanelPosition(responsePanelPosition);
}
break;
}
this.validate();
this.repaint();
}
/**
* Gets index of the {@link #requestPanel} in the tabbed pane of the current {@link #layout}.
*
* Note: This should be called prior changing the layout or position of the
* response panel, as it affects the actual position of request panel.
*
* @return the index of the request panel.
*/
private int getCurrentRequestTabIndex() {
if (layout == Layout.FULL) {
if (responsePanelPosition == ResponsePanelPosition.TAB_SIDE_BY_SIDE) {
return getTabbedFull().indexOfComponent(splitRequestAndResponsePanel);
}
return getTabbedFull().indexOfComponent(requestPanel);
}
if (responsePanelPosition == ResponsePanelPosition.TAB_SIDE_BY_SIDE) {
int idx = getTabbedWork().indexOfComponent(splitRequestAndResponsePanel);
if (idx != -1) {
return idx;
}
}
int idx = getTabbedWork().indexOfComponent(requestPanel);
if (idx != -1) {
return idx;
}
// Panel not shown yet, return "old" index.
return requestTabIndex;
}
/**
* Creates a split pane between the {@code status} area (bottom component) and {@code work} area
* or a split between {@code work} and {@code select} areas (top component), if the layout is
* {@link Layout#EXPAND_SELECT EXPAND_SELECT} or {@link Layout#EXPAND_STATUS EXPAND_STATUS},
* respectively.
*
* @return a {@code JSplitPane} between the {@code status} area and other areas
*/
private JSplitPane createStatusPanelsSplit() {
JSplitPane splitVert = new JSplitPane();
splitVert.setDividerLocation(restoreDividerLocation(DIVIDER_VERTICAL, 300));
splitVert.addPropertyChangeListener(
JSplitPane.DIVIDER_LOCATION_PROPERTY, new DividerResizedListener(DIVIDER_VERTICAL));
splitVert.setDividerSize(3);
splitVert.setOrientation(JSplitPane.VERTICAL_SPLIT);
splitVert.setResizeWeight(0.5D);
switch (layout) {
case EXPAND_SELECT:
splitVert.setTopComponent(getPaneWork());
break;
case EXPAND_STATUS:
default:
splitVert.setTopComponent(createSelectPanelsSplit());
break;
}
splitVert.setBottomComponent(getPaneStatus());
splitVert.setContinuousLayout(false);
splitVert.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
return splitVert;
}
/**
* Creates a split pane between the {@code select} area (left component) and {@code work} area
* or a split between {@code work} and {@code status} areas (right component), if the layout is
* {@link Layout#EXPAND_STATUS EXPAND_STATUS} or {@link Layout#EXPAND_SELECT EXPAND_SELECT},
* respectively.
*
* @return a {@code JSplitPane} between the {@code select} area and other areas
*/
private JSplitPane createSelectPanelsSplit() {
JSplitPane splitHoriz = new JSplitPane();
splitHoriz.setOneTouchExpandable(true);
splitHoriz.setLeftComponent(getPaneSelect());
switch (layout) {
case EXPAND_SELECT:
splitHoriz.setRightComponent(createStatusPanelsSplit());
break;
case EXPAND_STATUS:
default:
splitHoriz.setRightComponent(getPaneWork());
break;
}
splitHoriz.setDividerLocation(restoreDividerLocation(DIVIDER_HORIZONTAL, 300));
splitHoriz.addPropertyChangeListener(
JSplitPane.DIVIDER_LOCATION_PROPERTY,
new DividerResizedListener(DIVIDER_HORIZONTAL));
splitHoriz.setResizeWeight(0.3D);
splitHoriz.setContinuousLayout(false);
splitHoriz.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
return splitHoriz;
}
/**
* This method initializes paneStatus
*
* @return JPanel
*/
private JPanel getPaneStatus() {
if (paneStatus == null) {
paneStatus = new JPanel();
paneStatus.setLayout(new BorderLayout(0, 0));
paneStatus.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
paneStatus.add(getTabbedStatus());
}
return paneStatus;
}
/**
* This method initializes paneSelect
*
* @return JPanel
*/
private JPanel getPaneSelect() {
if (paneSelect == null) {
paneSelect = new JPanel();
paneSelect.setLayout(new BorderLayout(0, 0));
paneSelect.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
paneSelect.add(getTabbedSelect());
}
return paneSelect;
}
/**
* This method initializes paneWork, which is used for request/response/break/script console.
*
* @return JPanel
*/
private JPanel getPaneWork() {
if (paneWork == null) {
paneWork = new JPanel();
paneWork.setLayout(new BorderLayout(0, 0));
paneWork.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
paneWork.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
paneWork.add(getTabbedWork());
}
return paneWork;
}
private JPanel getFullLayoutPanel() {
if (fullLayoutPanel == null) {
fullLayoutPanel = new JPanel();
fullLayoutPanel.setLayout(new BorderLayout(0, 0));
fullLayoutPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
fullLayoutPanel.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
fullLayoutPanel.add(getTabbedFull());
}
return fullLayoutPanel;
}
private TabbedPanel2 getTabbedFull() {
if (tabbedFull == null) {
tabbedFull = new TabbedPanel2();
tabbedFull.setName("tabbedFull");
tabbedFull.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
}
return tabbedFull;
}
/**
* Gets the tabbed panel that has the {@link PanelType#STATUS STATUS} panels.
*
*
Direct access/manipulation of the tabbed panel is discouraged, the changes done to it
* might be lost while changing layouts.
*
* @return the tabbed panel of the {@code status} panels, never {@code null}
* @see #addPanel(AbstractPanel, PanelType)
*/
public TabbedPanel2 getTabbedStatus() {
if (tabbedStatus == null) {
tabbedStatus = new TabbedPanel2();
tabbedStatus.setPreferredSize(new Dimension(800, 200));
// ZAP: Move tabs to the top of the panel
tabbedStatus.setTabPlacement(JTabbedPane.TOP);
tabbedStatus.setName("tabbedStatus");
tabbedStatus.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
}
return tabbedStatus;
}
/**
* Gets the tabbed panel that has the {@link PanelType#WORK WORK} panels.
*
*
Direct access/manipulation of the tabbed panel is discouraged, the changes done to it
* might be lost while changing layouts.
*
* @return the tabbed panel of the {@code work} panels, never {@code null}
* @see #addPanel(AbstractPanel, PanelType)
*/
public TabbedPanel2 getTabbedWork() {
if (tabbedWork == null) {
tabbedWork = new TabbedPanel2();
tabbedWork.setPreferredSize(new Dimension(600, 400));
tabbedWork.setName("tabbedWork");
tabbedWork.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
}
return tabbedWork;
}
/**
* Sets whether or not the tabs should display the name of the panels.
*
*
The call to this method has not effect if the state is already set.
*
* @param showTabNames {@code true} if the names should be shown, {@code false} otherwise.
* @since 2.4.0
*/
public void toggleTabNames(boolean showTabNames) {
if (this.showTabNames == showTabNames) {
return;
}
this.showTabNames = showTabNames;
responseTabbedPanel.setShowTabNames(showTabNames);
if (layout != Layout.FULL) {
getTabbedStatus().setShowTabNames(showTabNames);
getTabbedSelect().setShowTabNames(showTabNames);
getTabbedWork().setShowTabNames(showTabNames);
} else {
getTabbedFull().setShowTabNames(showTabNames);
}
}
/**
* Gets the tabbed panel that has the {@link PanelType#SELECT SELECT} panels.
*
*
Direct access/manipulation of the tabbed panel is discouraged, the changes done to it
* might be lost while changing layouts.
*
* @return the tabbed panel of the {@code work} panels, never {@code null}
* @see #addPanel(AbstractPanel, PanelType)
*/
public TabbedPanel2 getTabbedSelect() {
if (tabbedSelect == null) {
tabbedSelect = new TabbedPanel2();
tabbedSelect.setPreferredSize(new Dimension(200, 400));
tabbedSelect.setName("tabbedSelect");
tabbedSelect.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
}
return tabbedSelect;
}
/**
* Adds the given panels to the workbench, hinting with the given panel type.
*
* @param panels the panels to add to the workbench
* @param panelType the type of the panels
* @throws IllegalArgumentException if any of the parameters is {@code null}.
* @since 2.5.0
* @see #removePanels(List, PanelType)
* @see #addPanel(AbstractPanel, PanelType)
*/
public void addPanels(List panels, PanelType panelType) {
validateNotNull(panels, "panels");
validateNotNull(panelType, "panelType");
boolean fullLayout = layout == Layout.FULL;
addPanels(getTabbedFull(), panels, fullLayout);
switch (panelType) {
case SELECT:
addPanels(getTabbedSelect(), panels, !fullLayout);
break;
case STATUS:
addPanels(getTabbedStatus(), panels, !fullLayout);
break;
case WORK:
addPanels(getTabbedWork(), panels, !fullLayout);
break;
default:
break;
}
}
/**
* Adds the given {@code panels} to the given {@code tabbedPanel} and whether they should be
* visible.
*
* After adding all the panels the tabbed panel is revalidated.
*
* @param tabbedPanel the tabbed panel to add the panels
* @param panels the panels to add
* @param visible {@code true} if the panel should be visible, {@code false} otherwise.
* @see #addPanel(TabbedPanel2, AbstractPanel, boolean)
* @see javax.swing.JComponent#revalidate()
*/
private static void addPanels(
TabbedPanel2 tabbedPanel, List panels, boolean visible) {
for (AbstractPanel panel : panels) {
addPanel(tabbedPanel, panel, visible);
}
tabbedPanel.revalidate();
}
/**
* Adds the given {@code panel} to the given {@code tabbedPanel}.
*
* @param tabbedPanel the tabbed panel to add the panel
* @param panel the panel to add
* @param visible {@code true} if the panel should be visible, {@code false} otherwise.
* @see #addPanels(TabbedPanel2, List, boolean)
*/
private static void addPanel(TabbedPanel2 tabbedPanel, AbstractPanel panel, boolean visible) {
if (visible) {
tabbedPanel.addTab(panel);
} else {
tabbedPanel.addTabHidden(panel);
}
}
/**
* Adds the given panel to the workbench, hinting with the given panel type.
*
* @param panel the panel to add to the workbench
* @param panelType the type of the panel
* @throws IllegalArgumentException if any of the parameters is {@code null}.
* @since 2.5.0
* @see #removePanel(AbstractPanel, PanelType)
* @see #addPanels(List, PanelType)
*/
public void addPanel(AbstractPanel panel, PanelType panelType) {
validateNotNull(panel, "panel");
validateNotNull(panelType, "panelType");
boolean fullLayout = layout == Layout.FULL;
addPanel(getTabbedFull(), panel, fullLayout);
switch (panelType) {
case SELECT:
addPanel(getTabbedSelect(), panel, !fullLayout);
getTabbedSelect().revalidate();
break;
case STATUS:
addPanel(getTabbedStatus(), panel, !fullLayout);
getTabbedStatus().revalidate();
break;
case WORK:
addPanel(getTabbedWork(), panel, !fullLayout);
getTabbedWork().revalidate();
break;
default:
break;
}
}
/**
* Removes the given panels of given panel type from the workbench panel.
*
* @param panels the panels to remove from the workbench panel
* @param panelType the type of the panels
* @throws IllegalArgumentException if any of the parameters is {@code null}.
* @since 2.5.0
* @see #addPanels(List, PanelType)
* @see #removePanel(AbstractPanel, PanelType)
*/
public void removePanels(List panels, PanelType panelType) {
validateNotNull(panels, "panels");
validateNotNull(panelType, "panelType");
removePanels(getTabbedFull(), panels);
switch (panelType) {
case SELECT:
removePanels(getTabbedSelect(), panels);
break;
case STATUS:
removePanels(getTabbedStatus(), panels);
break;
case WORK:
removePanels(getTabbedWork(), panels);
break;
default:
break;
}
}
/**
* Removes the given {@code panels} from the given {@code tabbedPanel}.
*
* After removing all the panels the tabbed panel is revalidated.
*
* @param tabbedPanel the tabbed panel to remove the panels
* @param panels the panels to remove
* @see #addPanel(TabbedPanel2, AbstractPanel, boolean)
* @see javax.swing.JComponent#revalidate()
*/
private static void removePanels(TabbedPanel2 tabbedPanel, List panels) {
for (AbstractPanel panel : panels) {
removeTabPanel(tabbedPanel, panel);
}
tabbedPanel.revalidate();
}
/**
* Removes the given {@code panel} from the given {@code tabbedPanel}.
*
* @param tabbedPanel the tabbed panel to remove the panel
* @param panel the panel to remove
* @see #removePanels(TabbedPanel2, List)
*/
private static void removeTabPanel(TabbedPanel2 tabbedPanel, AbstractPanel panel) {
tabbedPanel.removeTab(panel);
}
/**
* Removes the given panel of given panel type from the workbench panel.
*
* @param panel the panel to remove from the workbench panel
* @param panelType the type of the panel
* @throws IllegalArgumentException if any of the parameters is {@code null}.
* @since 2.5.0
* @see #addPanel(AbstractPanel, PanelType)
* @see #removePanels(List, PanelType)
*/
public void removePanel(AbstractPanel panel, PanelType panelType) {
validateNotNull(panel, "panel");
validateNotNull(panelType, "panelType");
removeTabPanel(getTabbedFull(), panel);
getTabbedFull().revalidate();
switch (panelType) {
case SELECT:
removeTabPanel(getTabbedSelect(), panel);
getTabbedSelect().revalidate();
break;
case STATUS:
removeTabPanel(getTabbedStatus(), panel);
getTabbedStatus().revalidate();
break;
case WORK:
removeTabPanel(getTabbedWork(), panel);
getTabbedWork().revalidate();
break;
default:
break;
}
}
/**
* Gets the panels that were added to the workbench with the given panel type.
*
* @param panelType the type of the panel
* @return a {@code List} with the panels of the given type
* @throws IllegalArgumentException if the given parameter is {@code null}.
* @since 2.5.0
*/
public List getPanels(PanelType panelType) {
validateNotNull(panelType, "panelType");
List panels = new ArrayList<>();
switch (panelType) {
case SELECT:
panels.addAll(getTabbedSelect().getPanels());
break;
case STATUS:
panels.addAll(getTabbedStatus().getPanels());
break;
case WORK:
panels.addAll(getTabbedWork().getPanels());
break;
default:
break;
}
return panels;
}
/**
* Gets the panels, sorted by name, that were added to the workbench with the given panel type.
*
* @param panelType the type of the panel
* @return a {@code List} with the sorted panels of the given type
* @throws IllegalArgumentException if the given parameter is {@code null}.
* @since 2.5.0
*/
public SortedSet getSortedPanels(PanelType panelType) {
validateNotNull(panelType, "panelType");
List panels = getPanels(panelType);
SortedSet sortedPanels =
new TreeSet<>(
new Comparator() {
@Override
public int compare(
AbstractPanel abstractPanel, AbstractPanel otherAbstractPanel) {
String name = abstractPanel.getName();
String otherName = otherAbstractPanel.getName();
if (name == null) {
if (otherName == null) {
return 0;
}
return -1;
} else if (otherName == null) {
return 1;
}
return name.compareTo(otherName);
}
});
sortedPanels.addAll(panels);
return sortedPanels;
}
/**
* Sets whether or not the panels should be visible.
*
* {@link AbstractPanel#isHideable() Non-hideable} and {@link AbstractPanel#isPinned()
* pinned} panels are not affected by this call, when set to not be visible.
*
* @param visible {@code true} if all panels should be visible, {@code false} otherwise.
* @since 2.5.0
*/
public void setPanelsVisible(boolean visible) {
if (layout == Layout.FULL) {
getTabbedFull().setPanelsVisible(visible);
} else {
getTabbedSelect().setPanelsVisible(visible);
getTabbedWork().setPanelsVisible(visible);
getTabbedStatus().setPanelsVisible(visible);
}
}
/**
* Pins all visible panels.
*
* @since 2.5.0
* @see #unpinVisiblePanels()
* @see AbstractPanel#setPinned(boolean)
*/
public void pinVisiblePanels() {
if (layout == Layout.FULL) {
getTabbedFull().pinVisibleTabs();
} else {
getTabbedSelect().pinVisibleTabs();
getTabbedWork().pinVisibleTabs();
getTabbedStatus().pinVisibleTabs();
}
}
/**
* Unpins all visible panels.
*
* @since 2.5.0
* @see #pinVisiblePanels()
* @see AbstractPanel#setPinned(boolean)
*/
public void unpinVisiblePanels() {
if (layout == Layout.FULL) {
getTabbedFull().unpinTabs();
} else {
getTabbedSelect().unpinTabs();
getTabbedWork().unpinTabs();
getTabbedStatus().unpinTabs();
}
}
/**
* Shows the given panel, if it was previously added.
*
*
It does nothing, if the tab is already shown.
*
* @param panel the panel to be shown
* @throws IllegalArgumentException if the given parameter is {@code null}.
* @since 2.5.0
* @see #addPanel(AbstractPanel, PanelType)
*/
public void showPanel(AbstractPanel panel) {
validateNotNull(panel, "panel");
if (layout == Layout.FULL) {
getTabbedFull().setVisible(panel, true);
} else {
getTabbedSelect().setVisible(panel, true);
getTabbedStatus().setVisible(panel, true);
getTabbedWork().setVisible(panel, true);
}
panel.setTabFocus();
}
/**
* Sets the position of the response panel.
*
* @param position the position of the response panel
* @throws IllegalArgumentException if the given parameter is {@code null}.
* @since 2.5.0
*/
void setResponsePanelPosition(ResponsePanelPosition position) {
validateNotNull(position, "position");
requestTabIndex = getCurrentRequestTabIndex();
responsePanelPosition = position;
if (layout == Layout.FULL) {
updateFullLayout();
return;
}
Component currentTabbedPanel = componentMaximiser.getMaximisedComponent();
if (componentMaximiser.isComponentMaximised()) {
componentMaximiser.unmaximiseComponent();
}
boolean selectRequest = removeSplitRequestAndResponsePanel(tabbedWork);
switch (position) {
case PANEL_ABOVE:
splitResponsePanelWithWorkTabbedPanel(JSplitPane.VERTICAL_SPLIT);
break;
case PANELS_SIDE_BY_SIDE:
splitResponsePanelWithWorkTabbedPanel(JSplitPane.HORIZONTAL_SPLIT);
break;
case TAB_SIDE_BY_SIDE:
splitResponsePanelWithRequestPanel(JSplitPane.HORIZONTAL_SPLIT, tabbedWork);
getPaneWork().removeAll();
getPaneWork().add(getTabbedWork());
getPaneWork().validate();
break;
case TABS_SIDE_BY_SIDE:
default:
if (currentTabbedPanel == responseTabbedPanel) {
currentTabbedPanel = tabbedWork;
}
insertResponseTab(tabbedWork);
getPaneWork().removeAll();
getPaneWork().add(getTabbedWork());
getPaneWork().validate();
}
if (selectRequest || getTabbedWork().getSelectedComponent() == null) {
getTabbedWork()
.setSelectedComponent(
responsePanelPosition == ResponsePanelPosition.TAB_SIDE_BY_SIDE
? splitRequestAndResponsePanel
: requestPanel);
}
if (currentTabbedPanel != null) {
componentMaximiser.maximiseComponent(currentTabbedPanel);
}
}
private void updateFullLayout() {
boolean selectRequest = removeSplitRequestAndResponsePanel(tabbedFull);
if (responsePanelPosition == ResponsePanelPosition.TAB_SIDE_BY_SIDE) {
splitResponsePanelWithRequestPanel(JSplitPane.HORIZONTAL_SPLIT, tabbedFull);
} else {
addRequestResponseTabs(tabbedFull);
}
getFullLayoutPanel().validate();
if (selectRequest || getTabbedFull().getSelectedComponent() == null) {
tabbedFull.setSelectedComponent(
responsePanelPosition == ResponsePanelPosition.TAB_SIDE_BY_SIDE
? splitRequestAndResponsePanel
: requestPanel);
}
}
private void addRequestResponseTabs(TabbedPanel2 tabbedPanel) {
tabbedPanel.addTab(requestPanel, requestTabIndex);
insertResponseTab(tabbedPanel);
}
private void insertResponseTab(TabbedPanel2 tabbedPanel) {
String tabName = getNormalisedTabName(responsePanel);
tabbedPanel.insertTab(
tabName,
DisplayUtils.getScaledIcon(responsePanel.getIcon()),
responsePanel,
tabName,
tabbedPanel.indexOfComponent(requestPanel) + 1);
}
private String getNormalisedTabName(AbstractPanel panel) {
return showTabNames ? panel.getName() : "";
}
private void splitResponsePanelWithWorkTabbedPanel(int orientation) {
responseTabbedPanel.hideAllTabs();
responseTabbedPanel.addTab(
getNormalisedTabName(responsePanel), responsePanel.getIcon(), responsePanel);
getPaneWork().removeAll();
JSplitPane split = createSplitPane(orientation);
split.setRightComponent(responseTabbedPanel);
split.setLeftComponent(getTabbedWork());
getPaneWork().add(split);
getPaneWork().validate();
}
private static JSplitPane createSplitPane(int orientation) {
JSplitPane splitPane = new JSplitPane(orientation);
splitPane.setDividerSize(3);
splitPane.setResizeWeight(0.5D);
splitPane.setContinuousLayout(false);
splitPane.setDoubleBuffered(true);
splitPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
return splitPane;
}
private void splitResponsePanelWithRequestPanel(int orientation, TabbedPanel2 tabbedPanel) {
if (splitRequestAndResponsePanel == null) {
splitRequestAndResponsePanel = new AbstractPanel();
splitRequestAndResponsePanel.setLayout(new BorderLayout());
splitRequestAndResponsePanel.setName(
Constant.messages.getString("http.panel.requestAndResponse.title"));
splitRequestAndResponsePanel.setIcon(
new ImageIcon(
WorkbenchPanel.class.getResource("/resource/icon/16/handshake.png")));
splitRequestAndResponsePanel.setHideable(false);
splitRequestAndResponse = createSplitPane(orientation);
splitRequestAndResponsePanel.add(splitRequestAndResponse);
}
Component selectedComponent = tabbedPanel.getSelectedComponent();
splitRequestAndResponse.setLeftComponent(requestPanel);
tabbedPanel.removeTab(requestPanel);
splitRequestAndResponse.setRightComponent(responsePanel);
tabbedPanel.removeTab(responsePanel);
tabbedPanel.addTab(splitRequestAndResponsePanel, requestTabIndex);
if (selectedComponent == requestPanel || selectedComponent == responsePanel) {
tabbedPanel.setSelectedComponent(splitRequestAndResponsePanel);
}
}
private boolean removeSplitRequestAndResponsePanel(TabbedPanel2 tabbedPanel) {
boolean selectRequest = false;
int pos = tabbedPanel.indexOfComponent(splitRequestAndResponsePanel);
if (pos != -1) {
selectRequest = tabbedPanel.getSelectedComponent() == splitRequestAndResponsePanel;
tabbedPanel.removeTab(splitRequestAndResponsePanel);
addRequestResponseTabs(tabbedPanel);
}
return selectRequest;
}
/**
* @param prefix
* @param location
*/
private void saveDividerLocation(String prefix, int location) {
if (location > 0) {
LOGGER.debug(
"Saving preference {}{}.{}={}",
prefnzPrefix,
prefix,
PREF_DIVIDER_LOCATION,
location);
this.preferences.put(
prefnzPrefix + prefix + "." + PREF_DIVIDER_LOCATION,
Integer.toString(location));
// immediate flushing
try {
this.preferences.flush();
} catch (final BackingStoreException e) {
LOGGER.error("Error while saving the preferences", e);
}
}
}
/**
* @param prefix
* @param fallback
* @return the size of the frame OR fallback value, if there wasn't any preference.
*/
private int restoreDividerLocation(String prefix, int fallback) {
int result = fallback;
final String sizestr =
preferences.get(prefnzPrefix + prefix + "." + PREF_DIVIDER_LOCATION, null);
if (sizestr != null) {
int location = 0;
try {
location = Integer.parseInt(sizestr.trim());
} catch (final Exception e) {
// ignoring, cause is prevented by default values;
}
if (location > 0) {
result = location;
LOGGER.debug(
"Restoring preference {}{}.{}={}",
prefnzPrefix,
prefix,
PREF_DIVIDER_LOCATION,
location);
}
}
return result;
}
/*
* ========================================================================
*/
private final class DividerResizedListener implements PropertyChangeListener {
private final String prefix;
public DividerResizedListener(String prefix) {
super();
assert prefix != null;
this.prefix = prefix;
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
JSplitPane component = (JSplitPane) evt.getSource();
if (component != null) {
LOGGER.debug(
"{}{}.location={}", prefnzPrefix, prefix, component.getDividerLocation());
saveDividerLocation(prefix, component.getDividerLocation());
}
}
}
}