1+ from javax .swing import JTabbedPane , JPanel , JButton , JLabel , SwingConstants , JOptionPane , GroupLayout , JCheckBox , JSplitPane
2+ from javax .swing .event import ChangeListener , DocumentListener
3+ from javax .swing .LayoutStyle .ComponentPlacement import RELATED , UNRELATED
4+ from java .awt import BorderLayout , Font , Component
5+ from java .beans import PropertyChangeListener
6+ from org .python .core .util import StringUtil
7+ from uicomponents import BurpUI , TabComponent , TabComponentEditableTabMixin , TabComponentCloseableMixin , TabComponentCloseListener , TabComponentTitleChangedListener
8+ from models import ObservableCollection , Script
9+ from utils import EditorFileAdapter
10+
11+
12+ class ScriptTabbedPane (JTabbedPane ):
13+
14+ EMPTY_SCRIPT_TEXT = '''<html><body>
15+ <div style="font-size: 16pt;text-align:center">
16+ You have no Python scripts created.<br/> Please use the add tab (+) button to create a new Python script.
17+ </div></body></html>'''
18+
19+ def __init__ (self , extender , callbacks , helpers , scripts ):
20+ super (ScriptTabbedPane , self ).__init__ ()
21+ self .scripts = scripts
22+ self .extender = extender
23+ self .callbacks = callbacks
24+ self .helpers = helpers
25+
26+ self .addChangeListener (ScriptTabbedPane .TabsStateChanged ())
27+ self .scripts .add_listener (self )
28+ self .create_add_tab ()
29+
30+ def create_add_tab (self ):
31+ self .add_tab = JButton ("+" , actionPerformed = self .add_clicked )
32+ self .add_tab .font = Font ('Monospaced' , Font .PLAIN , 18 )
33+ self .add_tab .contentAreaFilled = False
34+ self .add_tab .border = None
35+
36+ self .emptyTab = JPanel (BorderLayout ())
37+ self .emptyTab .add (JLabel (ScriptTabbedPane .EMPTY_SCRIPT_TEXT , SwingConstants .CENTER ), BorderLayout .CENTER )
38+
39+ self .addTab (None , self .emptyTab )
40+ self .setTabComponentAt (0 , self .add_tab )
41+
42+ def add_clicked (self , event ):
43+ idx = self .tabCount - 1
44+ title = 'New Script {}' .format (idx + 1 )
45+ script = Script (self .extender , self .callbacks , self .helpers , title )
46+ self .scripts .add (script )
47+
48+ def create_script_tab (self , script , idx ):
49+ new_tab = ScriptTabComponent (script )
50+ new_tab .tabbed_pane = self
51+ new_tab .addCloseListener (ScriptTabbedPane .ScriptTabCloseListener (self , self .scripts , script ))
52+ new_tab .addTitleChangedListener (ScriptTabbedPane .ScriptTabTabTitleChangedListener (script ))
53+ new_panel = ScriptPanel (script , self .callbacks )
54+ self .add (new_panel , idx )
55+ self .setTabComponentAt (idx , new_tab )
56+ self .selectedIndex = idx
57+
58+ def collection_changed (self , collection , type , script ):
59+ if type == ObservableCollection .ITEM_ADDED :
60+ idx = self .tabCount - 1
61+ self .create_script_tab (script , idx )
62+
63+
64+ class ScriptTabCloseListener (TabComponentCloseListener ):
65+
66+ def __init__ (self , tabbedpane , scripts , script ):
67+ self .tabbed_pane = tabbedpane
68+ self .scripts = scripts
69+ self .script = script
70+
71+ def tabClose (self , event ):
72+ result = JOptionPane .showConfirmDialog (None , 'Are you sure you want to close \' {}\' ?' .format (event .source .text ),
73+ "Close Tab" ,
74+ JOptionPane .YES_NO_OPTION ,
75+ JOptionPane .QUESTION_MESSAGE )
76+ if result == JOptionPane .YES_OPTION :
77+ idx = self .tabbed_pane .indexOfTabComponent (event .source )
78+ self .tabbed_pane .remove (idx )
79+ self .scripts .remove (self .script )
80+
81+ class ScriptTabTabTitleChangedListener (TabComponentTitleChangedListener ):
82+
83+ def __init__ (self , script ):
84+ self .script = script
85+
86+ def titleChanged (self , event ):
87+ self .script .title = event .getTitle ()
88+
89+
90+ class TabsStateChanged (ChangeListener ):
91+
92+ def stateChanged (self , event ):
93+ # prevents the add tab from being selected apart from when there are no other tabs created (i.e. starting from fresh)
94+ # instead the tab next the add tab is selected
95+ tabbed_pane = event .source
96+ if tabbed_pane .tabCount > 1 and tabbed_pane .selectedIndex == tabbed_pane .tabCount - 1 :
97+ tabbed_pane .selectedIndex = tabbed_pane .tabCount - 2
98+
99+
100+ class ScriptTabComponent (TabComponentEditableTabMixin , TabComponentCloseableMixin , TabComponent ):
101+
102+ def __init__ (self , script ):
103+ super (ScriptTabComponent , self ).__init__ ()
104+ self .script = script
105+ self .text = self .script .title
106+ self .close_button .font = Font ('Dialog' , Font .PLAIN , 16 )
107+ self .text_field .toolTipText = self .toolTipText = 'Double click to rename, Enter to confirm or Esc to cancel'
108+
109+
110+
111+ class ScriptEditingPanel (JPanel , DocumentListener ):
112+
113+ def __init__ (self , callbacks , script ):
114+ super (ScriptEditingPanel , self ).__init__ ()
115+ self .callbacks = callbacks
116+ self .script = script
117+ self .enabledCheckbox = JCheckBox ('Enabled' , self .script .enabled , itemStateChanged = self .enabled_changed , alignmentX = Component .LEFT_ALIGNMENT )
118+ self .scriptEditor = callbacks .createTextEditor ()
119+ self .scriptEditor .text = script .content
120+ self .scriptText = self .scriptEditor .component
121+ self .compileButton = JButton ('Compile' , actionPerformed = self .compile , enabled = False )
122+
123+ editingLayout = GroupLayout (self , autoCreateGaps = True , autoCreateContainerGaps = True )
124+ editingLayout .setHorizontalGroup (editingLayout .createParallelGroup ()
125+ .addGroup (editingLayout .createSequentialGroup ()
126+ .addComponent (self .enabledCheckbox )
127+ .addPreferredGap (UNRELATED )
128+ )
129+ .addGroup (editingLayout .createParallelGroup ()
130+ .addComponent (self .scriptText )
131+ .addComponent (self .compileButton )
132+ )
133+ )
134+
135+ editingLayout .setVerticalGroup (editingLayout .createSequentialGroup ()
136+ .addGroup (editingLayout .createParallelGroup ()
137+ .addComponent (self .enabledCheckbox )
138+ )
139+ .addGroup (editingLayout .createSequentialGroup ()
140+ .addComponent (self .scriptText )
141+ .addComponent (self .compileButton )
142+ )
143+ )
144+ self .layout = editingLayout
145+ BurpUI .get_textarea (self .scriptEditor ).document .addDocumentListener (self )
146+ self .compile (None )
147+
148+ def enabled_changed (self , event ):
149+ self .script .enabled = self .enabledCheckbox .isSelected ()
150+
151+ def compile (self , event ):
152+ self .script .compile ()
153+ self .compileButton .enabled = False
154+
155+ def changedUpdate (self , event ):
156+ self ._update_content ()
157+ self ._can_compile (event )
158+
159+ def insertUpdate (self , event ):
160+ self ._update_content ()
161+ self ._can_compile (event )
162+
163+ def removeUpdate (self , event ):
164+ self ._update_content ()
165+ self ._can_compile (event )
166+
167+ def _update_content (self ):
168+ self .script .content = StringUtil .fromBytes (self .scriptEditor .text )
169+
170+ def _can_compile (self , event ):
171+ self .compileButton .enabled = False
172+ if event .document .length > 0 :
173+ self .compileButton .enabled = self .script .requires_compile
174+
175+
176+ class ScriptOutputPanel (JPanel , PropertyChangeListener ):
177+
178+ def __init__ (self , callbacks , script ):
179+ super (ScriptOutputPanel , self ).__init__ ()
180+ self .callbacks = callbacks
181+ self .script = script
182+ self .script .addPropertyChangeListener (self )
183+ self .tabbedPane = JTabbedPane ()
184+ self ._create_output_panel ()
185+ self ._create_error_panel ()
186+ self .tabbedPane .addTab ('Output' , self .outputPanel )
187+ self .tabbedPane .addTab ('Errors' , self .errorPanel )
188+ self .layout = BorderLayout ()
189+ self .add (self .tabbedPane , BorderLayout .CENTER )
190+ self .script .stdout = EditorFileAdapter (self .outputEditor )
191+ self .script .stderr = EditorFileAdapter (self .errorEditor )
192+
193+ def clear_stderr (self , event ):
194+ self .errorEditor .text = ''
195+
196+ def clear_stdout (self , event ):
197+ self .outputEditor .text = ''
198+
199+ def propertyChange (self , event ):
200+ if event .propertyName == Script .Properties .IS_COMPILED :
201+ if event .newValue :
202+ self .errorEditor .text = ''
203+ elif event .propertyName == Script .Properties .COMPILATION_ERROR :
204+ self .errorEditor .text = event .newValue
205+ self .tabbedPane .selectedIndex = 1
206+
207+ def _create_output_panel (self ):
208+ self .outputPanel = JPanel ()
209+ self .outputEditor = self .callbacks .createTextEditor ()
210+ self .outputEditor .editable = False
211+ self .outputText = self .outputEditor .component
212+ self .clearOutputButton = JButton ('Clear' , actionPerformed = self .clear_stdout )
213+
214+ outputLayout = GroupLayout (self .outputPanel , autoCreateGaps = True , autoCreateContainerGaps = True )
215+ outputLayout .setHorizontalGroup (outputLayout .createParallelGroup ()
216+ .addComponent (self .outputText )
217+ .addComponent (self .clearOutputButton )
218+ )
219+
220+ outputLayout .setVerticalGroup (outputLayout .createSequentialGroup ()
221+ .addComponent (self .outputText )
222+ .addComponent (self .clearOutputButton )
223+ )
224+ self .outputPanel .layout = outputLayout
225+
226+ def _create_error_panel (self ):
227+ self .errorPanel = JPanel ()
228+ self .errorEditor = self .callbacks .createTextEditor ()
229+ self .errorEditor .editable = False
230+ self .errorText = self .errorEditor .component
231+ self .clearErrorButton = JButton ('Clear' , actionPerformed = self .clear_stderr )
232+
233+ errorLayout = GroupLayout (self .errorPanel , autoCreateGaps = True , autoCreateContainerGaps = True )
234+ errorLayout .setHorizontalGroup (errorLayout .createParallelGroup ()
235+ .addComponent (self .errorText )
236+ .addComponent (self .clearErrorButton )
237+ )
238+ errorLayout .setVerticalGroup (errorLayout .createSequentialGroup ()
239+ .addComponent (self .errorText )
240+ .addComponent (self .clearErrorButton )
241+ )
242+ self .errorPanel .layout = errorLayout
243+
244+
245+ class ScriptPanel (JPanel ):
246+
247+ def __init__ (self , script , callbacks ):
248+ self .script = script
249+ self .layout = BorderLayout ()
250+ self .editingPanel = ScriptEditingPanel (callbacks , script )
251+ self .outputPanel = ScriptOutputPanel (callbacks , script )
252+ self .splitPane = JSplitPane (JSplitPane .VERTICAL_SPLIT , dividerSize = 10 )
253+ self .splitPane .topComponent = self .editingPanel
254+ self .splitPane .bottomComponent = self .outputPanel
255+ self .add (self .splitPane , BorderLayout .CENTER )
256+
257+
258+ class GUI (object ):
259+
260+ def __init__ (self , extender , callbacks , helpers , scripts ):
261+ self .panel = JPanel ()
262+ self .tabs = ScriptTabbedPane (extender , callbacks , helpers , scripts )
263+ layout = BorderLayout ()
264+ self .panel .setLayout (layout )
265+ self .panel .add (self .tabs )
266+
267+ def build (self ):
268+ return self .panel
0 commit comments