View Javadoc
1   /*******************************************************************************
2    * Copyhacked (H) 2012-2025.
3    * This program and the accompanying materials
4    * are made available under no term at all, use it like
5    * you want, but share and discuss it
6    * every time possible with every body.
7    * 
8    * Contributors:
9    *      ron190 at ymail dot com - initial implementation
10   ******************************************************************************/
11  package com.jsql.view.swing;
12  
13  import com.jsql.model.InjectionModel;
14  import com.jsql.util.*;
15  import com.jsql.view.swing.action.HotkeyUtil;
16  import com.jsql.view.subscriber.SubscriberView;
17  import com.jsql.view.swing.menubar.AppMenubar;
18  import com.jsql.view.swing.panel.PanelAddressBar;
19  import com.jsql.view.swing.panel.split.SplitNS;
20  import com.jsql.view.swing.terminal.AbstractExploit;
21  import com.jsql.view.swing.tab.TabManagers;
22  import com.jsql.view.swing.util.MediatorHelper;
23  import com.jsql.view.swing.util.UiUtil;
24  import org.apache.commons.lang3.SystemUtils;
25  import org.apache.logging.log4j.LogManager;
26  import org.apache.logging.log4j.Logger;
27  
28  import javax.swing.*;
29  import java.awt.*;
30  import java.awt.event.WindowAdapter;
31  import java.awt.event.WindowEvent;
32  import java.util.HashMap;
33  import java.util.Map;
34  import java.util.UUID;
35  import java.util.prefs.Preferences;
36  import java.util.stream.Stream;
37  
38  /**
39   * View in the MVC pattern, defines all the components
40   * and process actions sent by the model.<br>
41   * Main groups of components:<br>
42   * - at the top: textfield inputs,<br>
43   * - at the center: tree on the left, table on the right,<br>
44   * - at the bottom: information labels.
45   */
46  public class JFrameView extends JFrame {
47  
48      private static final Logger LOGGER = LogManager.getRootLogger();
49  
50      /**
51       * Map of terminal by unique identifier.
52       */
53      private final Map<UUID, AbstractExploit> mapUuidShell = new HashMap<>();
54      private final transient SubscriberView subscriberView = new SubscriberView();
55      private TabManagers tabManagers;
56      private boolean isMaximized = false;
57      private final InjectionModel injectionModel;
58      private SplitNS splitNS;  // main
59  
60      public JFrameView(InjectionModel injectionModel) {  // Build the GUI: add app icon, tree icons, the 3 main panels
61          super(StringUtil.APP_NAME);
62          this.injectionModel = injectionModel;
63          MediatorHelper.register(this);
64          UiUtil.prepareGUI();  // Load UI before any component
65          this.initPaneComponents();
66          this.initWindow();
67          this.initShortcuts();
68          this.displayVersion();
69          I18nUtil.checkCurrentLanguage();
70          this.check4K();
71  
72          SwingUtilities.invokeLater(() -> {  // paint native blu svg in theme color behind the scene
73              AppMenubar.applyTheme(injectionModel.getMediatorUtils().preferencesUtil().getThemeFlatLafName());  // refresh missing components
74              if (injectionModel.getMediatorUtils().proxyUtil().isNotLive(GitUtil.ShowOnConsole.YES)) {  // network access
75                  return;
76              }
77              if (injectionModel.getMediatorUtils().preferencesUtil().isCheckingUpdate()) {
78                  injectionModel.getMediatorUtils().gitUtil().checkUpdate(GitUtil.ShowOnConsole.NO);
79              }
80              if (injectionModel.getMediatorUtils().preferencesUtil().isShowNews()) {  // disabled when UT only
81                  injectionModel.getMediatorUtils().gitUtil().showNews();
82              }
83              this.setVisible(true);
84              MediatorHelper.panelAddressBar().getTextFieldAddress().requestFocusInWindow();  // required here to get focus
85          });
86      }
87  
88      private void initPaneComponents() {
89          // Define the default panel: each component on a vertical line
90          this.getContentPane().setLayout(new BoxLayout(this.getContentPane(), BoxLayout.PAGE_AXIS));
91  
92          this.tabManagers = new TabManagers();  // Tab manager linked to cards
93          this.add(this.tabManagers);
94  
95          var menubar = new AppMenubar();
96          this.setJMenuBar(menubar);
97          MediatorHelper.register(menubar);
98  
99          var panelAddressBar = new PanelAddressBar();  // Textfield at the top
100         MediatorHelper.register(panelAddressBar);
101         this.add(panelAddressBar);
102 
103         var mainPanel = new JPanel(new BorderLayout());  // Main panel for tree and tables in the middle
104         this.splitNS = new SplitNS();
105         mainPanel.add(this.splitNS);
106         this.add(mainPanel);
107 
108         menubar.getMenuWindows().switchLocaleFromPreferences();
109     }
110 
111     private void initWindow() {
112         this.setIconImages(UiUtil.getIcons());  // define small and large app icons
113         var preferences = Preferences.userRoot().node(InjectionModel.class.getName());
114         this.addWindowStateListener(e -> this.isMaximized = (e.getNewState() & Frame.MAXIMIZED_BOTH) == Frame.MAXIMIZED_BOTH);
115         this.addWindowListener(new WindowAdapter() {
116             @Override
117             public void windowOpened(WindowEvent event) {
118                 double nsProportion = preferences.getDouble(PreferencesUtil.NS_SPLIT, 0.75);
119                 double ewProportion = preferences.getDouble(PreferencesUtil.EW_SPLIT, 0.33);
120                 if (preferences.getBoolean(PreferencesUtil.IS_MAXIMIZED, false)) {
121                     JFrameView.this.setExtendedState(Frame.MAXIMIZED_BOTH);
122                 }
123                 // Proportion means different location relative to whether frame is maximized or not
124                 // Maximizing must wait AWT events to reflect the new divider location proportion
125                 SwingUtilities.invokeLater(() -> {
126                     JFrameView.this.splitNS.setDividerLocation(Math.clamp(nsProportion, 0.0, 1.0));
127                     JFrameView.this.splitNS.getSplitEW().setDividerLocation(Math.clamp(ewProportion, 0.0, 1.0));
128                     MediatorHelper.panelConsoles().getNetworkSplitPane().setDividerLocation(
129                         ComponentOrientation.getOrientation(I18nUtil.getCurrentLocale()).isLeftToRight()
130                         ? 0.33
131                         : 0.66
132                     );
133                 });
134             }
135 
136             @Override
137             public void windowClosing(WindowEvent e) {
138                 preferences.putBoolean(PreferencesUtil.IS_MAXIMIZED, JFrameView.this.isMaximized);
139 
140                 int ewDividerLocation = JFrameView.this.splitNS.getSplitEW().getDividerLocation();
141                 int ewHeight = JFrameView.this.splitNS.getSplitEW().getWidth();
142                 double ewProportion = 100.0 * ewDividerLocation / ewHeight;
143                 double ewLocationProportionCapped = Math.clamp(ewProportion / 100.0, 0.0, 1.0);
144 
145                 int nsDividerLocation = JFrameView.this.splitNS.getDividerLocation();
146                 int nsHeight = JFrameView.this.splitNS.getHeight();
147                 double nsProportion = 100.0 * nsDividerLocation / nsHeight;
148                 double nsLocationProportionCapped = Math.clamp(nsProportion / 100.0, 0.0, 1.0);
149 
150                 // Divider location changes when window is maximized, instead stores location percentage between 0.0 and 1.0
151                 preferences.putDouble(PreferencesUtil.NS_SPLIT, nsLocationProportionCapped);
152                 preferences.putDouble(PreferencesUtil.EW_SPLIT, ewLocationProportionCapped);
153 
154                 preferences.putBoolean(PreferencesUtil.BINARY_VISIBLE, false);
155                 preferences.putBoolean(PreferencesUtil.CHUNK_VISIBLE, false);
156                 preferences.putBoolean(PreferencesUtil.NETWORK_VISIBLE, false);
157                 preferences.putBoolean(PreferencesUtil.JAVA_VISIBLE, false);
158                 for (var i = 0 ; i < MediatorHelper.tabConsoles().getTabCount() ; i++) {
159                     if ("CONSOLE_BINARY_LABEL".equals(MediatorHelper.tabConsoles().getTabComponentAt(i).getName())) {
160                         preferences.putBoolean(PreferencesUtil.BINARY_VISIBLE, true);
161                     } else if ("CONSOLE_CHUNK_LABEL".equals(MediatorHelper.tabConsoles().getTabComponentAt(i).getName())) {
162                         preferences.putBoolean(PreferencesUtil.CHUNK_VISIBLE, true);
163                     } else if ("CONSOLE_NETWORK_LABEL".equals(MediatorHelper.tabConsoles().getTabComponentAt(i).getName())) {
164                         preferences.putBoolean(PreferencesUtil.NETWORK_VISIBLE, true);
165                     } else if ("CONSOLE_JAVA_LABEL".equals(MediatorHelper.tabConsoles().getTabComponentAt(i).getName())) {
166                         preferences.putBoolean(PreferencesUtil.JAVA_VISIBLE, true);
167                     }
168                 }
169             }
170         });
171         
172         this.setSize(1024, 768);
173         this.setLocationRelativeTo(null);  // center the window
174         this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
175     }
176 
177     private void initShortcuts() {
178         HotkeyUtil.addShortcut(this.getRootPane(), MediatorHelper.tabResults());
179         HotkeyUtil.addTextFieldShortcutSelectAll();
180     }
181 
182     public void resetInterface() {  // Empty the interface
183         this.mapUuidShell.clear();
184         MediatorHelper.panelAddressBar().getPanelTrailingAddress().reset();
185         MediatorHelper.panelConsoles().reset();
186         MediatorHelper.treeDatabase().reset();
187         
188         for (var i = 0 ; i < MediatorHelper.tabConsoles().getTabCount() ; i++) {
189             var tabComponent = MediatorHelper.tabConsoles().getTabComponentAt(i);
190             if (tabComponent != null) {
191                 tabComponent.setFont(tabComponent.getFont().deriveFont(Font.PLAIN));
192             }
193         }
194         
195         Stream.of(MediatorHelper.managerFile(), MediatorHelper.managerExploit()).forEach(managerList -> {
196             managerList.setButtonEnable(false);
197             managerList.changePrivilegeIcon(UiUtil.SQUARE.getIcon());
198         });
199     }
200 
201     private void displayVersion() {
202         LOGGER.log(
203             LogLevelUtil.CONSOLE_DEFAULT,
204             "{} v{} on Java {}-{}-{}",
205             () -> StringUtil.APP_NAME,
206             () -> this.injectionModel.getPropertiesUtil().getVersionJsql(),
207             () -> SystemUtils.JAVA_VERSION,
208             () -> SystemUtils.OS_ARCH,
209             () -> SystemUtils.USER_LANGUAGE
210         );
211     }
212 
213     private void check4K() {
214         Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
215         int width = (int) screenSize.getWidth();
216         if (width >= 3840 && !this.injectionModel.getMediatorUtils().preferencesUtil().is4K()) {
217             LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Screen compatible with resolution 4K, enable high-definition in Preferences");
218         }
219     }
220 
221 
222     // Getters and setters
223 
224     public final Map<UUID, AbstractExploit> getMapUuidShell() {
225         return this.mapUuidShell;
226     }
227 
228     public SubscriberView getSubscriberView() {
229         return this.subscriberView;
230     }
231 
232     public SplitNS getSplitNS() {
233         return this.splitNS;
234     }
235 
236     public TabManagers getTabManagers() {
237         return this.tabManagers;
238     }
239 }