-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Renameable, closable, tabs styled like Burp.
- Loading branch information
Showing
2 changed files
with
249 additions
and
0 deletions.
There are no files selected for viewing
217 changes: 217 additions & 0 deletions
217
src/main/java/com/codemagi/burp/ui/BurpTabComponent.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,217 @@ | ||
package com.codemagi.burp.ui; | ||
|
||
import com.codemagi.burp.BaseExtender; | ||
import java.awt.*; | ||
import java.awt.event.ActionEvent; | ||
import java.awt.event.ActionListener; | ||
import java.awt.event.FocusAdapter; | ||
import java.awt.event.FocusEvent; | ||
import java.awt.event.FocusListener; | ||
import java.awt.event.MouseAdapter; | ||
import java.awt.event.MouseEvent; | ||
import java.awt.event.MouseListener; | ||
|
||
import javax.swing.*; | ||
import javax.swing.plaf.LayerUI; | ||
import javax.swing.plaf.basic.BasicButtonUI; | ||
|
||
/** | ||
* Component to be used as tabComponent: Renders instead of a default tab name. | ||
* | ||
* Contains a JLabel to show the (editable) tab name and a JButton to close the tab. | ||
* | ||
* @author August Detlefsen [augustd at codemagi dot com] | ||
*/ | ||
public class BurpTabComponent extends JPanel { | ||
|
||
private final JTabbedPane pane; //the pane of tabs that will contain this one | ||
private final JTextField editor = new JTextField(); | ||
private static final Color TRANSPARENT = new Color(255, 255, 255, 0); | ||
|
||
public BurpTabComponent(String title, final JTabbedPane pane) { | ||
//unset default FlowLayout' gaps | ||
super(new FlowLayout(FlowLayout.LEFT, 0, 0)); | ||
if (pane == null) { | ||
throw new NullPointerException("TabbedPane is null"); | ||
} | ||
this.pane = pane; | ||
|
||
//create an editor component | ||
editor.setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 7)); | ||
editor.setText(title); | ||
editor.setBackground(TRANSPARENT); | ||
editor.setOpaque(false); | ||
editor.setEditable(false); | ||
BaseExtender.getCallbacks().customizeUiComponent(editor); | ||
add(new JLayer<JTextField>(editor, new DispatchEventLayerUI())); | ||
|
||
//Make editor not editable until double-click | ||
editor.setEditable(false); | ||
editor.setEnabled(false); | ||
editor.setDisabledTextColor(Color.BLACK); | ||
editor.addMouseListener(MOUSE_CLICK_LISTENER); | ||
editor.addFocusListener(FOCUS_LOST_LISTENER); | ||
|
||
//Add the close button | ||
JButton button = new TabButton(); | ||
add(button); | ||
|
||
//add more space to the top of the component | ||
setBorder(BorderFactory.createEmptyBorder(2, 0, 0, 0)); | ||
setBackground(TRANSPARENT); | ||
setOpaque(false); | ||
} | ||
|
||
private class TabButton extends JButton implements ActionListener { | ||
|
||
public TabButton() { | ||
int size = 9; | ||
setPreferredSize(new Dimension(size, size)); | ||
setToolTipText("close this tab"); | ||
//Make the button looks the same for all Laf's | ||
setUI(new BasicButtonUI()); | ||
//Make it transparent | ||
setContentAreaFilled(false); | ||
//No need to be focusable | ||
setFocusable(false); | ||
setBorder(BorderFactory.createLineBorder(Color.BLACK, 1)); | ||
setBorderPainted(false); | ||
this.setMargin(new Insets(11, 6, 0, 0)); | ||
|
||
//Making nice rollover effect | ||
//we use the same listener for all buttons | ||
addMouseListener(BUTTON_MOUSE_LISTENER); | ||
setRolloverEnabled(true); | ||
//Close the proper tab by clicking the button | ||
addActionListener(this); | ||
BaseExtender.getCallbacks().customizeUiComponent(this); | ||
} | ||
|
||
@Override | ||
public void actionPerformed(ActionEvent e) { | ||
int i = pane.indexOfTabComponent(BurpTabComponent.this); | ||
if (i != -1) { | ||
pane.remove(i); | ||
} | ||
} | ||
|
||
//we don't want to update UI for this button | ||
@Override | ||
public void updateUI() { | ||
} | ||
|
||
//paint the cross | ||
@Override | ||
protected void paintComponent(Graphics g) { | ||
super.paintComponent(g); | ||
Graphics2D g2 = (Graphics2D) g.create(); | ||
//shift the image for pressed buttons | ||
if (getModel().isPressed()) { | ||
g2.translate(1, 1); | ||
} | ||
g2.setStroke(new BasicStroke(1)); | ||
g2.setColor(Color.GRAY); | ||
if (getModel().isRollover()) { | ||
g2.setColor(Color.BLACK); | ||
} | ||
int delta = 6; | ||
g2.drawLine(2, 3, 6, 7); | ||
g2.drawLine(2, 7, 6, 3); | ||
g2.dispose(); | ||
} | ||
} | ||
|
||
/** | ||
* Create rollover effect for X button | ||
*/ | ||
private final static MouseListener BUTTON_MOUSE_LISTENER = new MouseAdapter() { | ||
@Override | ||
public void mouseEntered(MouseEvent e) { | ||
Component component = e.getComponent(); | ||
if (component instanceof AbstractButton) { | ||
AbstractButton button = (AbstractButton) component; | ||
button.setBorderPainted(true); | ||
} | ||
} | ||
|
||
@Override | ||
public void mouseExited(MouseEvent e) { | ||
Component component = e.getComponent(); | ||
if (component instanceof AbstractButton) { | ||
AbstractButton button = (AbstractButton) component; | ||
button.setBorderPainted(false); | ||
} | ||
} | ||
}; | ||
|
||
private final MouseListener MOUSE_CLICK_LISTENER = new MouseAdapter() { | ||
@Override | ||
public void mouseClicked(MouseEvent e) { | ||
System.out.println("MOUSE_CLICK_LISTENER.mouseClicked()"); | ||
|
||
Component component = e.getComponent(); | ||
component.requestFocusInWindow(); | ||
|
||
//no matter how many clicks, always select the tab | ||
System.out.println("single-click"); | ||
System.out.println("index: " + pane.indexOfTabComponent(BurpTabComponent.this)); | ||
pane.setSelectedIndex(pane.indexOfTabComponent(BurpTabComponent.this)); | ||
|
||
if (e.getClickCount() == 2) { | ||
System.out.println("double-click"); | ||
JTextField field = (JTextField) component; | ||
field.setEditable(true); | ||
field.setEnabled(true); | ||
} | ||
} | ||
}; | ||
|
||
/** | ||
* Make label field un-editable when it loses focus | ||
*/ | ||
private static final FocusListener FOCUS_LOST_LISTENER = new FocusAdapter() { | ||
@Override | ||
public void focusLost(FocusEvent e) { | ||
Component component = e.getComponent(); | ||
System.out.println("User exited"); | ||
JTextField field = (JTextField) component; | ||
field.setEditable(false); | ||
field.setEnabled(false); | ||
} | ||
}; | ||
|
||
class DispatchEventLayerUI extends LayerUI<JTextField> { | ||
|
||
@Override | ||
public void installUI(JComponent c) { | ||
super.installUI(c); | ||
if (c instanceof JLayer) { | ||
((JLayer) c).setLayerEventMask(AWTEvent.MOUSE_MOTION_EVENT_MASK); | ||
} | ||
} | ||
|
||
@Override | ||
public void uninstallUI(JComponent c) { | ||
if (c instanceof JLayer) { | ||
((JLayer) c).setLayerEventMask(0); | ||
} | ||
super.uninstallUI(c); | ||
} | ||
|
||
/** | ||
* Pass mouse motion events (in/out) to the underlying tabbed pane to | ||
* handle highlighting of the tab. | ||
* | ||
* @param e | ||
* @param l | ||
*/ | ||
@Override | ||
protected void processMouseMotionEvent(MouseEvent e, JLayer<? extends JTextField> l) { | ||
Component src = e.getComponent(); | ||
Container tgt = SwingUtilities.getAncestorOfClass(JTabbedPane.class, src); | ||
tgt.dispatchEvent(SwingUtilities.convertMouseEvent(src, e, tgt)); | ||
} | ||
|
||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package com.codemagi.burp.ui; | ||
|
||
import java.awt.Component; | ||
import javax.swing.JTabbedPane; | ||
|
||
/** | ||
* Creates a JTabbedPane customized to work with Burp. Includes: | ||
* <li>Ability to rename tabs</li> | ||
* <li>Ability to close tabs</li> | ||
* <li>Burp-like UI</li> | ||
* | ||
* @author August Detlefsen [augustd at codemagi dot com] | ||
*/ | ||
public class BurpTabbedPane extends JTabbedPane { | ||
|
||
/** | ||
* Add a new re-nameable, closable tab to the tabbed pane. | ||
* | ||
* @param name | ||
* @param component | ||
* @return | ||
*/ | ||
@Override | ||
public Component add(String name, Component component) { | ||
//add the component that renders when the tab is selected | ||
Component output = super.add(name, component); | ||
//add the tab component: renders instead of the default tab name | ||
setTabComponentAt(indexOfComponent(component), new BurpTabComponent(name, this)); | ||
return output; | ||
} | ||
|
||
} |