1
2
3
4
5
6
7
8
9
10
11 package com.jsql.view.swing.tab;
12
13 import com.formdev.flatlaf.extras.FlatSVGIcon;
14 import com.jsql.model.bean.database.AbstractElementDatabase;
15 import com.jsql.util.I18nUtil;
16 import com.jsql.util.LogLevelUtil;
17 import com.jsql.util.StringUtil;
18 import com.jsql.util.ThreadUtil;
19 import com.jsql.util.reverse.ModelReverse;
20 import com.jsql.view.swing.action.ActionCloseTabResult;
21 import com.jsql.view.swing.action.HotkeyUtil;
22 import com.jsql.view.swing.popupmenu.JPopupMenuText;
23 import com.jsql.view.swing.tab.dnd.DnDTabbedPane;
24 import com.jsql.view.swing.tab.dnd.TabTransferHandler;
25 import com.jsql.view.swing.table.PanelTable;
26 import com.jsql.view.swing.terminal.AbstractExploit;
27 import com.jsql.view.swing.terminal.ExploitReverseShell;
28 import com.jsql.view.swing.text.JPopupTextArea;
29 import com.jsql.view.swing.text.JTextFieldPlaceholder;
30 import com.jsql.view.swing.util.*;
31 import org.apache.commons.lang3.StringUtils;
32 import org.apache.logging.log4j.LogManager;
33 import org.apache.logging.log4j.Logger;
34 import org.jsoup.Jsoup;
35 import org.jsoup.safety.Safelist;
36
37 import javax.swing.*;
38 import javax.swing.event.HyperlinkEvent;
39 import javax.swing.text.DefaultEditorKit;
40 import java.awt.*;
41 import java.awt.datatransfer.StringSelection;
42 import java.awt.event.*;
43 import java.io.IOException;
44 import java.net.MalformedURLException;
45 import java.net.URISyntaxException;
46 import java.util.Arrays;
47 import java.util.EmptyStackException;
48 import java.util.List;
49 import java.util.UUID;
50 import java.util.function.BiConsumer;
51 import java.util.function.IntConsumer;
52
53
54
55
56 public class TabResults extends DnDTabbedPane {
57
58 private static final Logger LOGGER = LogManager.getRootLogger();
59
60 public static final String TAB_EXPLOIT_FAILURE_INCORRECT_URL = "Tab exploit failure: incorrect URL";
61 public static final String UDF_SHELL = "UDF shell";
62 public static final String SQL_SHELL = "SQL shell";
63 public static final String REVERSE_SHELL = "Reverse shell";
64 public static final String WEB_SHELL = "webShell";
65 public static final String TAB_RESULTS = "tabResults";
66
67
68
69
70 public TabResults() {
71 this.setName(TabResults.TAB_RESULTS);
72 this.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
73 this.setTransferHandler(new TabTransferHandler());
74 this.putClientProperty("JTabbedPane.tabClosable", true);
75 this.putClientProperty("JTabbedPane.tabCloseCallback", (IntConsumer) ActionCloseTabResult::perform);
76 UIManager.put("TabbedPane.closeHoverForeground", LogLevelUtil.COLOR_RED);
77 HotkeyUtil.addShortcut(this);
78 this.addMouseWheelListener(new TabbedPaneMouseWheelListener());
79 MediatorHelper.register(this);
80 }
81
82 public void addFileTab(String label, String content, String path) {
83 MediatorHelper.frame().getSplitNS().invokeLaterWithSplitOrientation(() -> {
84 JTextArea fileText = new JPopupTextArea().getProxy();
85 fileText.setText(content);
86 fileText.setFont(new Font(UiUtil.FONT_NAME_MONO_NON_ASIAN, Font.PLAIN, 14));
87 fileText.setCaretPosition(0);
88 this.addTextTab(label, path, fileText, UiUtil.DOWNLOAD.getIcon());
89 MediatorHelper.tabManagersCards().addToLists(path, label);
90 });
91 }
92
93 public void addAdminTab(String urlSuccess) {
94 MediatorHelper.frame().getSplitNS().invokeLaterWithSplitOrientation(() -> {
95 String htmlSource = StringUtils.EMPTY;
96
97
98
99
100 try {
101
102
103
104 htmlSource = Jsoup.clean(
105 Jsoup.connect(urlSuccess)
106
107 .ignoreContentType(true)
108
109 .ignoreHttpErrors(true)
110 .get()
111 .html()
112 .replaceAll("<img[^>]*>", StringUtils.EMPTY)
113 .replaceAll("<input[^>]+type=\"?hidden[^>]+>", StringUtils.EMPTY)
114 .replaceAll("<input[^>]+type=\"?(submit|button)[^>]+>", "<div style=\"background-color:#eeeeee;text-align:center;border:1px solid black;width:100px;\">button</div>")
115 .replaceAll("<input[^>]*>", "<div style=\"text-align:center;border:1px solid black;width:100px;\">input</div>"),
116 Safelist.relaxed()
117 .addTags("center", "div", "span")
118 .addAttributes(":all", "style")
119 );
120 } catch (IOException e) {
121 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Failure opening page: {}", e.getMessage());
122 } catch (ExceptionInInitializerError | NoClassDefFoundError e) {
123 LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
124 }
125
126 final var browser = new JTextPane();
127 browser.setContentType("text/html");
128 browser.setEditable(false);
129 browser.setCaretPosition(0);
130
131
132
133 try {
134 browser.setText(htmlSource);
135 } catch (IndexOutOfBoundsException | EmptyStackException e) {
136 LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
137 }
138
139 JMenuItem itemCopyUrl = new JMenuItem(I18nUtil.valueByKey("CONTEXT_MENU_COPY_PAGE_URL"));
140 I18nViewUtil.addComponentForKey("CONTEXT_MENU_COPY_PAGE_URL", itemCopyUrl);
141
142 JMenuItem itemCopy = new JMenuItem();
143 itemCopy.setAction(browser.getActionMap().get(DefaultEditorKit.copyAction));
144 itemCopy.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_DOWN_MASK));
145 itemCopy.setMnemonic('C');
146 itemCopy.setText(I18nUtil.valueByKey("CONTEXT_MENU_COPY"));
147 I18nViewUtil.addComponentForKey("CONTEXT_MENU_COPY", itemCopy);
148
149 JMenuItem itemSelectAll = new JMenuItem();
150 itemSelectAll.setAction(browser.getActionMap().get(DefaultEditorKit.selectAllAction));
151 itemSelectAll.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_DOWN_MASK));
152 itemSelectAll.setText(I18nUtil.valueByKey("CONTEXT_MENU_SELECT_ALL"));
153 I18nViewUtil.addComponentForKey("CONTEXT_MENU_SELECT_ALL", itemSelectAll);
154 itemSelectAll.setMnemonic('A');
155
156 final var menu = new JPopupMenu();
157 menu.add(itemCopyUrl);
158 menu.add(new JSeparator());
159 menu.add(itemCopy);
160 menu.add(itemSelectAll);
161 menu.applyComponentOrientation(ComponentOrientation.getOrientation(I18nUtil.getCurrentLocale()));
162
163 itemCopyUrl.addActionListener(actionEvent -> {
164 var stringSelection = new StringSelection(urlSuccess);
165 var clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
166 clipboard.setContents(stringSelection, null);
167 });
168
169 itemSelectAll.addActionListener(actionEvent -> browser.selectAll());
170
171 browser.addFocusListener(new FocusAdapter() {
172 @Override
173 public void focusGained(FocusEvent focusEvent) {
174 browser.getCaret().setVisible(true);
175 browser.getCaret().setSelectionVisible(true);
176 }
177 });
178 browser.addMouseListener(new BrowserMouseAdapter(browser, menu));
179
180 final var scroller = new JScrollPane(browser);
181 MediatorHelper.tabResults().addTab(urlSuccess.replaceAll(".+/", StringUtils.EMPTY) + StringUtils.SPACE, scroller);
182
183
184 try {
185 MediatorHelper.tabResults().setSelectedComponent(scroller);
186 } catch (ArrayIndexOutOfBoundsException | IllegalArgumentException e) {
187 LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
188 }
189 MediatorHelper.tabResults().setToolTipTextAt(
190 MediatorHelper.tabResults().indexOfComponent(scroller),
191 String.format("<html>%s</html>", urlSuccess)
192 );
193
194
195 var header = new TabHeader(
196 urlSuccess.replaceAll(".+/", StringUtils.EMPTY),
197 UiUtil.ADMIN.getIcon()
198 );
199 MediatorHelper.tabResults().setTabComponentAt(MediatorHelper.tabResults().indexOfComponent(scroller), header);
200 browser.setCaretPosition(0);
201
202 MediatorHelper.tabResults().updateUI();
203 });
204 }
205
206 public void addReportTab(String content) {
207 MediatorHelper.frame().getSplitNS().invokeLaterWithSplitOrientation(() -> {
208 JEditorPane editorPane = new JEditorPane();
209 editorPane.setContentType("text/html");
210 editorPane.setText("<html><span style=\"white-space: nowrap; font-family:'"+ UiUtil.FONT_NAME_MONO_NON_ASIAN +"'\">" + content + "</span></html>");
211 editorPane.setFont(UIManager.getFont("TextArea.font"));
212 editorPane.setDragEnabled(true);
213 editorPane.setEditable(false);
214 editorPane.setCaretPosition(0);
215 editorPane.getCaret().setBlinkRate(0);
216 editorPane.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
217 editorPane.setComponentPopupMenu(new JPopupMenuText(editorPane));
218 editorPane.addHyperlinkListener(linkEvent -> {
219 if (HyperlinkEvent.EventType.ACTIVATED.equals(linkEvent.getEventType())) {
220 try {
221 Desktop.getDesktop().browse(linkEvent.getURL().toURI());
222 } catch (IOException | URISyntaxException | UnsupportedOperationException e) {
223 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Browsing to URL failed", e);
224 }
225 }
226 });
227 editorPane.addFocusListener(new FocusAdapter() {
228 @Override
229 public void focusGained(FocusEvent focusEvent) {
230 editorPane.getCaret().setVisible(true);
231 editorPane.getCaret().setSelectionVisible(true);
232 editorPane.getCaret().setBlinkRate(0);
233 }
234 });
235 UiUtil.init(editorPane);
236
237 this.addTextTab("Vulnerability report", "Analysis report with all payloads detected", editorPane, UiUtil.APP_ICON.getIcon());
238 });
239 }
240
241 public void addTextTab(String label, String toolTipText, JComponent componentText, FlatSVGIcon icon) {
242 var scroller = new JScrollPane(componentText);
243 this.addTab(label + StringUtils.SPACE, scroller);
244 this.setSelectedComponent(scroller);
245 this.setToolTipTextAt(this.indexOfComponent(scroller), toolTipText);
246 var header = new TabHeader(label, icon);
247 this.setTabComponentAt(this.indexOfComponent(scroller), header);
248
249 this.updateUI();
250 }
251
252 public void addTabExploitWeb(String url) {
253 MediatorHelper.frame().getSplitNS().invokeLaterWithSplitOrientation(() -> {
254 try {
255 var terminalID = UUID.randomUUID();
256 var terminal = new AbstractExploit(terminalID, url, "web") {
257 @Override
258 public void action(String command, UUID terminalID, String urlShell, String... arg) {
259 MediatorHelper.model().getResourceAccess().runWebShell(command, terminalID, urlShell);
260 }
261 };
262 terminal.setName(TabResults.WEB_SHELL);
263 MediatorHelper.frame().getMapUuidShell().put(terminalID, terminal);
264
265 JPanel panelTerminalWithReverse = this.getTerminalWithMenu(terminal);
266 this.addTab("Web shell", panelTerminalWithReverse);
267 this.setSelectedComponent(panelTerminalWithReverse);
268
269 var header = new TabHeader("Web shell", UiUtil.TERMINAL.getIcon());
270 this.setTabComponentAt(this.indexOfComponent(panelTerminalWithReverse), header);
271 terminal.requestFocusInWindow();
272
273 this.updateUI();
274 } catch (MalformedURLException | URISyntaxException e) {
275 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, TabResults.TAB_EXPLOIT_FAILURE_INCORRECT_URL, e);
276 }
277 });
278 }
279
280 public void addTabExploitReverseShell(String port) {
281 MediatorHelper.frame().getSplitNS().invokeLaterWithSplitOrientation(() -> {
282 try {
283 var terminalID = UUID.randomUUID();
284 var terminal = new ExploitReverseShell(terminalID, port);
285 MediatorHelper.frame().getMapUuidShell().put(terminalID, terminal);
286
287 JScrollPane scroller = new JScrollPane(terminal);
288 this.addTab(TabResults.REVERSE_SHELL, scroller);
289 this.setSelectedComponent(scroller);
290
291 var header = new TabHeader(TabResults.REVERSE_SHELL, UiUtil.TERMINAL.getIcon());
292 this.setTabComponentAt(this.indexOfComponent(scroller), header);
293 terminal.requestFocusInWindow();
294
295 this.updateUI();
296 } catch (URISyntaxException | IOException e) {
297 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, TabResults.TAB_EXPLOIT_FAILURE_INCORRECT_URL, e);
298 }
299 });
300 }
301
302 public void addTabExploitUdf(BiConsumer<String, UUID> biConsumerRunCmd) {
303 MediatorHelper.frame().getSplitNS().invokeLaterWithSplitOrientation(() -> {
304 try {
305 var terminalID = UUID.randomUUID();
306 var terminal = new AbstractExploit(terminalID, null, "udf") {
307 @Override
308 public void action(String command, UUID terminalID, String urlShell, String... arg) {
309 biConsumerRunCmd.accept(command, terminalID);
310 }
311 };
312 MediatorHelper.frame().getMapUuidShell().put(terminalID, terminal);
313
314 JPanel panelTerminalWithReverse = this.getTerminalWithMenu(terminal);
315 this.addTab(TabResults.UDF_SHELL, panelTerminalWithReverse);
316 this.setSelectedComponent(panelTerminalWithReverse);
317
318 var header = new TabHeader(TabResults.UDF_SHELL, UiUtil.TERMINAL.getIcon());
319 this.setTabComponentAt(this.indexOfComponent(panelTerminalWithReverse), header);
320 terminal.requestFocusInWindow();
321
322 this.updateUI();
323 } catch (MalformedURLException | URISyntaxException e) {
324 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, TabResults.TAB_EXPLOIT_FAILURE_INCORRECT_URL, e);
325 }
326 });
327 }
328
329 public void addTabExploitSql(String url, String user, String pass) {
330 MediatorHelper.frame().getSplitNS().invokeLaterWithSplitOrientation(() -> {
331 try {
332 var terminalID = UUID.randomUUID();
333 var terminal = new AbstractExploit(terminalID, url, "sql") {
334 @Override
335 public void action(String cmd, UUID terminalID, String wbhPath, String... arg) {
336 MediatorHelper.model().getResourceAccess().runSqlShell(cmd, terminalID, wbhPath, arg[0], arg[1]);
337 }
338 };
339 terminal.setLoginPassword(new String[]{ user, pass });
340 MediatorHelper.frame().getMapUuidShell().put(terminalID, terminal);
341
342 JScrollPane scroller = new JScrollPane(terminal);
343 this.addTab(TabResults.SQL_SHELL, scroller);
344 this.setSelectedComponent(scroller);
345
346 var header = new TabHeader(TabResults.SQL_SHELL, UiUtil.TERMINAL.getIcon());
347 this.setTabComponentAt(this.indexOfComponent(scroller), header);
348 terminal.requestFocusInWindow();
349
350 this.updateUI();
351 } catch (MalformedURLException | URISyntaxException e) {
352 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, TabResults.TAB_EXPLOIT_FAILURE_INCORRECT_URL, e);
353 }
354 });
355 }
356
357 public void addTabValues(String[][] data, String[] columnNames, AbstractElementDatabase table) {
358 var panelTable = new PanelTable(data, columnNames);
359
360 this.addTab(StringUtil.detectUtf8(table.toString()), panelTable);
361 panelTable.setComponentOrientation(ComponentOrientation.getOrientation(I18nUtil.getCurrentLocale()));
362
363 this.setSelectedComponent(panelTable);
364
365 var header = new TabHeader(UiStringUtil.detectUtf8Html(table.toString()), UiUtil.TABLE_BOLD.getIcon());
366 this.setTabComponentAt(this.indexOfComponent(panelTable), header);
367
368 this.updateUI();
369 }
370
371 private JPanel getTerminalWithMenu(AbstractExploit terminal) {
372 JPanel panelTerminalWithReverse = new JPanel() {
373 @Override
374 public boolean isOptimizedDrawingEnabled() {
375 return false;
376 }
377 };
378 OverlayLayout overlay = new OverlayLayout(panelTerminalWithReverse);
379 panelTerminalWithReverse.setLayout(overlay);
380
381 var panelReverseMargin = new JPanel();
382 panelReverseMargin.setLayout(new BoxLayout(panelReverseMargin, BoxLayout.LINE_AXIS));
383 panelReverseMargin.setOpaque(false);
384 panelReverseMargin.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 15));
385
386 var menuReverse = new JLabel(TabResults.REVERSE_SHELL, UiUtil.ARROW_DOWN.getIcon(), SwingConstants.LEFT);
387 menuReverse.setName(TabResults.REVERSE_SHELL);
388 menuReverse.addMouseListener(new MouseAdapter() {
389 @Override
390 public void mousePressed(MouseEvent e) {
391 var popupMenu = TabResults.this.showMenu(terminal);
392 popupMenu.updateUI();
393 popupMenu.show(e.getComponent(), e.getComponent().getX(),5 + e.getComponent().getY() + e.getComponent().getHeight());
394 popupMenu.setLocation(e.getComponent().getLocationOnScreen().x,5 + e.getComponent().getLocationOnScreen().y + e.getComponent().getHeight());
395 }
396 });
397 menuReverse.setMaximumSize(menuReverse.getPreferredSize());
398 JScrollPane scrollerTerminal = new JScrollPane(terminal);
399 scrollerTerminal.setAlignmentX(1f);
400 scrollerTerminal.setAlignmentY(0f);
401 panelReverseMargin.setAlignmentX(1f);
402 panelReverseMargin.setAlignmentY(0f);
403 panelReverseMargin.add(menuReverse);
404 panelTerminalWithReverse.add(panelReverseMargin);
405 panelTerminalWithReverse.add(scrollerTerminal);
406
407 return panelTerminalWithReverse;
408 }
409
410 private JPopupMenu showMenu(AbstractExploit terminal) {
411 JPopupMenu menuReverse = new JPopupMenu();
412
413 var menuListen = new JMenu("Listen");
414 menuListen.setComponentOrientation(
415 ComponentOrientation.RIGHT_TO_LEFT.equals(ComponentOrientation.getOrientation(I18nUtil.getCurrentLocale()))
416 ? ComponentOrientation.LEFT_TO_RIGHT
417 : ComponentOrientation.RIGHT_TO_LEFT
418 );
419 var panelPublicAddress = new JPanel(new BorderLayout());
420 panelPublicAddress.add(new JLabel("<html><b>Your public address (listener) :</b></html>"));
421 menuListen.add(panelPublicAddress);
422 menuListen.add(new JSeparator());
423 var address = new JTextFieldPlaceholder("Local IP/domain", "10.0.2.2");
424 menuListen.add(address);
425 var port = new JTextFieldPlaceholder("Local port", "4444");
426 menuListen.add(port);
427
428 var panelServerConnection = new JPanel(new BorderLayout());
429 panelServerConnection.add(new JLabel("<html><b>Server method (connector) :</b></html>"));
430 menuListen.add(panelServerConnection);
431 menuListen.add(new JSeparator());
432 var buttonGroup = new ButtonGroup();
433 List<ModelReverse> commandsReverse = MediatorHelper.model().getMediatorUtils().preferencesUtil().getCommandsReverse();
434 commandsReverse.forEach(modelReverse -> {
435 var radio = new RadioItemNonClosing(modelReverse.getName());
436 radio.setActionCommand(modelReverse.getName());
437 radio.setSelected("bash".equals(modelReverse.getName()));
438 buttonGroup.add(radio);
439 menuListen.add(radio);
440 });
441
442 Runnable runnableReverse = () -> {
443 ThreadUtil.sleep(2500);
444 MediatorHelper.model().getMediatorUtils().preferencesUtil().getCommandsReverse().stream()
445 .filter(modelReverse -> modelReverse.getName().equals(buttonGroup.getSelection().getActionCommand()))
446 .findFirst()
447 .ifPresent(modelReverse -> MediatorHelper.model().getResourceAccess().runWebShell(
448 String.format(modelReverse.getCommand(), address.getText(), port.getText()),
449 null,
450 terminal.getUrlShell(),
451 true
452 ));
453 };
454
455 var panelOpenIn = new JPanel(new BorderLayout());
456 panelOpenIn.add(new JLabel("<html><b>Open In :</b></html>"));
457 menuListen.add(panelOpenIn);
458 menuListen.add(new JSeparator());
459
460 var menuBuiltInShell = new RadioItemNonClosing("Built-in shell", true);
461 var menuExternalShell = new RadioItemNonClosing("External listening shell");
462 var buttonTypeShell = new ButtonGroup();
463 buttonTypeShell.add(menuBuiltInShell);
464 buttonTypeShell.add(menuExternalShell);
465 menuListen.add(menuBuiltInShell);
466 menuListen.add(menuExternalShell);
467 menuListen.add(new JSeparator());
468 var panelCreate = new JPanel(new BorderLayout());
469 panelCreate.add(new JButton(new AbstractAction("Create reverse shell") {
470 @Override
471 public void actionPerformed(ActionEvent e) {
472 if (menuBuiltInShell.isSelected()) {
473 MediatorHelper.tabResults().addTabExploitReverseShell(port.getText());
474 }
475 new Thread(runnableReverse).start();
476 menuReverse.setVisible(false);
477 }
478 }));
479 menuListen.add(panelCreate);
480
481 var menuConnect = new JMenu("Connect");
482 menuConnect.setComponentOrientation(
483 ComponentOrientation.RIGHT_TO_LEFT.equals(ComponentOrientation.getOrientation(I18nUtil.getCurrentLocale()))
484 ? ComponentOrientation.LEFT_TO_RIGHT
485 : ComponentOrientation.RIGHT_TO_LEFT
486 );
487 var panelServerPublicAddress = new JPanel(new BorderLayout());
488 panelServerPublicAddress.add(new JLabel("<html><b>Server public address (listener) :</b></html>"));
489 menuConnect.add(panelServerPublicAddress);
490 menuConnect.add(new JSeparator());
491 menuConnect.add(new JTextFieldPlaceholder("Target IP/domain"));
492 menuConnect.add(new JTextFieldPlaceholder("Target port"));
493 menuConnect.add(new JSeparator());
494
495 var panelServerListeningConnection = new JPanel(new BorderLayout());
496 panelServerListeningConnection.add(new JLabel("<html><b>Server listening method :</b></html>"));
497 menuConnect.add(panelServerListeningConnection);
498 var buttonGroupListening = new ButtonGroup();
499 List.of("netcat").forEach(method -> {
500 var radio = new JRadioButtonMenuItem(method) {
501 @Override
502 protected void processMouseEvent(MouseEvent evt) {
503 if (evt.getID() == MouseEvent.MOUSE_RELEASED && this.contains(evt.getPoint())) {
504 this.doClick();
505 this.setArmed(true);
506 } else {
507 super.processMouseEvent(evt);
508 }
509 }
510 };
511 radio.setSelected("netcat".equals(method));
512 buttonGroupListening.add(radio);
513 menuConnect.add(radio);
514 });
515 menuConnect.add(new JSeparator());
516 menuConnect.add(new JMenuItem("Create"));
517
518 menuReverse.add(menuListen);
519 menuReverse.add(menuConnect);
520 return menuReverse;
521 }
522
523 private static class BrowserMouseAdapter extends MouseAdapter {
524 private final JTextPane browser;
525 private final JPopupMenu menu;
526
527 public BrowserMouseAdapter(JTextPane browser, JPopupMenu menu) {
528 this.browser = browser;
529 this.menu = menu;
530 }
531
532 @Override
533 public void mousePressed(MouseEvent evt) {
534 this.browser.requestFocusInWindow();
535 if (evt.isPopupTrigger()) {
536 this.menu.show(evt.getComponent(), evt.getX(), evt.getY());
537 }
538 }
539
540 @Override
541 public void mouseReleased(MouseEvent evt) {
542 if (evt.isPopupTrigger()) {
543 Arrays.stream(this.menu.getComponents()).map(a -> (JComponent) a).forEach(JComponent::updateUI);
544 this.menu.updateUI();
545
546 try {
547 this.menu.show(evt.getComponent(), evt.getX(), evt.getY());
548 } catch (IllegalComponentStateException e) {
549 LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
550 }
551 this.menu.setLocation(
552 ComponentOrientation.RIGHT_TO_LEFT.equals(ComponentOrientation.getOrientation(I18nUtil.getCurrentLocale()))
553 ? evt.getXOnScreen() - this.menu.getWidth()
554 : evt.getXOnScreen(),
555 evt.getYOnScreen()
556 );
557 }
558 }
559 }
560 }