View Javadoc
1   package com.jsql.view.swing.menubar;
2   
3   import com.formdev.flatlaf.intellijthemes.FlatDarkFlatIJTheme;
4   import com.formdev.flatlaf.intellijthemes.FlatHighContrastIJTheme;
5   import com.formdev.flatlaf.intellijthemes.FlatLightFlatIJTheme;
6   import com.formdev.flatlaf.intellijthemes.materialthemeuilite.FlatMTGitHubDarkIJTheme;
7   import com.formdev.flatlaf.intellijthemes.materialthemeuilite.FlatMTGitHubIJTheme;
8   import com.formdev.flatlaf.themes.FlatMacDarkLaf;
9   import com.formdev.flatlaf.themes.FlatMacLightLaf;
10  import com.jsql.util.I18nUtil;
11  import com.jsql.util.PreferencesUtil;
12  import com.jsql.view.swing.action.ActionNewWindow;
13  import com.jsql.view.swing.dialog.translate.Language;
14  import com.jsql.view.swing.panel.PanelPreferences;
15  import com.jsql.view.swing.sql.SqlEngine;
16  import com.jsql.view.swing.tab.TabHeader;
17  import com.jsql.view.swing.util.I18nViewUtil;
18  import com.jsql.view.swing.util.MediatorHelper;
19  import com.jsql.view.swing.util.RadioItemNonClosing;
20  import com.jsql.view.swing.util.UiUtil;
21  
22  import javax.swing.*;
23  import java.awt.*;
24  import java.awt.event.ActionEvent;
25  import java.awt.event.InputEvent;
26  import java.awt.event.KeyEvent;
27  import java.awt.event.MouseEvent;
28  import java.util.AbstractMap;
29  import java.util.Arrays;
30  import java.util.Locale;
31  import java.util.concurrent.atomic.AtomicBoolean;
32  import java.util.concurrent.atomic.AtomicInteger;
33  
34  public class MenuWindows extends JMenu {
35  
36      private static final String I18N_SQL_ENGINE = "MENUBAR_SQL_ENGINE";
37      private static final String I18N_PREFERENCES = "MENUBAR_PREFERENCES";
38      private final AppMenubar appMenubar;
39  
40      private final JMenu menuView;
41  
42      public MenuWindows(AppMenubar appMenubar) {
43          super(I18nUtil.valueByKey("MENUBAR_WINDOWS"));
44          this.appMenubar = appMenubar;
45  
46          this.setName("menuWindows");
47          I18nViewUtil.addComponentForKey("MENUBAR_WINDOWS", this);
48          this.setMnemonic('W');
49  
50          JMenuItem itemNewWindows = new JMenuItem(new ActionNewWindow());
51          I18nViewUtil.addComponentForKey("NEW_WINDOW_MENU", itemNewWindows);
52  
53          this.add(itemNewWindows);
54          var menuAppearance = new JMenu(I18nUtil.valueByKey("MENUBAR_APPEARANCE"));
55          I18nViewUtil.addComponentForKey("MENUBAR_APPEARANCE", menuAppearance);
56          menuAppearance.setMnemonic('A');
57  
58          JMenuItem itemNewWindows4k = new JMenuItem(
59              new ActionNewWindow("Open window in 4K resolution...", "-Dsun.java2d.uiScale=2.5")
60          );
61          menuAppearance.add(itemNewWindows4k);
62  
63          var groupRadio = new ButtonGroup();
64          var menuThemes = new JMenu(I18nUtil.valueByKey("MENUBAR_THEMES"));
65          I18nViewUtil.addComponentForKey("MENUBAR_THEMES", menuAppearance);
66          menuThemes.setMnemonic('T');
67  
68          Arrays.asList(
69              new AbstractMap.SimpleEntry<>(FlatLightFlatIJTheme.class.getName(), "IntelliJ"),
70              new AbstractMap.SimpleEntry<>(FlatDarkFlatIJTheme.class.getName(), "IntelliJ Dark"),
71              new AbstractMap.SimpleEntry<>(FlatMacLightLaf.class.getName(), "macOS"),
72              new AbstractMap.SimpleEntry<>(FlatMacDarkLaf.class.getName(), "macOS Dark"),
73              new AbstractMap.SimpleEntry<>(FlatMTGitHubIJTheme.class.getName(), "GitHub"),
74              new AbstractMap.SimpleEntry<>(FlatMTGitHubDarkIJTheme.class.getName(), "GitHub Dark"),
75              new AbstractMap.SimpleEntry<>(FlatHighContrastIJTheme.class.getName(), "High contrast")
76          ).forEach(entry -> {
77              JMenuItem item = new RadioItemNonClosing(
78                  new AbstractAction() {
79                      @Override
80                      public void actionPerformed(ActionEvent e) {
81                          AppMenubar.applyTheme(entry.getKey());
82                      }
83                  }
84              );
85              item.setText(entry.getValue());
86              item.setSelected(entry.getKey().equals(MediatorHelper.model().getMediatorUtils().getPreferencesUtil().getThemeFlatLafName()));
87              groupRadio.add(item);
88              menuThemes.add(item);
89          });
90  
91          this.add(itemNewWindows);
92          this.add(menuAppearance);
93          this.add(menuThemes);
94          this.add(new JSeparator());
95          this.add(this.initMenuTranslation());
96          this.add(new JSeparator());
97  
98          this.menuView = new JMenu(I18nUtil.valueByKey("MENUBAR_VIEW"));
99          I18nViewUtil.addComponentForKey("MENUBAR_VIEW", this.menuView);
100         this.menuView.setMnemonic('V');
101 
102         AtomicInteger accelerator = new AtomicInteger(0x31);
103         AtomicInteger tabPosition = new AtomicInteger();
104         MediatorHelper.frame().getTabManagers().getIconsTabs().forEach(entry -> {
105             var menuItem = new JMenuItem(I18nUtil.valueByKey(entry.getKeyLabel()), entry.getIcon());
106             I18nViewUtil.addComponentForKey(entry.getKeyLabel(), menuItem);
107             menuItem.setName(entry.getKeyLabel());  // required by card manager switch
108             this.menuView.add(menuItem);
109 
110             menuItem.setAccelerator(KeyStroke.getKeyStroke(
111                 KeyEvent.getExtendedKeyCodeForChar(accelerator.getAndIncrement()),
112                 InputEvent.CTRL_DOWN_MASK
113             ));
114 
115             final var position = tabPosition.get();  // required by closure
116             menuItem.addActionListener(actionEvent -> {  // setAction() could set action+text+icon but i18n not easy
117                 CardLayout cardLayout = (CardLayout) MediatorHelper.tabManagersCards().getLayout();
118                 cardLayout.show(MediatorHelper.tabManagersCards(), menuItem.getName());
119                 MediatorHelper.frame().getTabManagers().setSelectedIndex(position);
120             });
121             tabPosition.getAndIncrement();
122         });
123 
124         this.add(this.menuView);
125 
126         var menuPanel = new JMenu(I18nUtil.valueByKey("MENUBAR_PANEL"));
127         I18nViewUtil.addComponentForKey("MENUBAR_PANEL", menuPanel);
128         menuPanel.setMnemonic('C');
129 
130         Arrays.asList(
131             new ModelCheckboxMenu(
132                 "CONSOLE_CHUNK_LABEL",
133                 PreferencesUtil.CHUNK_VISIBLE,
134                 () -> MediatorHelper.panelConsoles().insertChunkTab(),
135                 UiUtil.CHUNK.getIcon()
136             ),
137             new ModelCheckboxMenu(
138                 "CONSOLE_BINARY_LABEL",
139                 PreferencesUtil.BINARY_VISIBLE,
140                 () -> MediatorHelper.panelConsoles().insertBooleanTab(),
141                 UiUtil.BINARY.getIcon()
142             ),
143             new ModelCheckboxMenu(
144                 "CONSOLE_NETWORK_LABEL",
145                 PreferencesUtil.NETWORK_VISIBLE,
146                 () -> MediatorHelper.panelConsoles().insertNetworkTab(),
147                 UiUtil.NETWORK.getIcon()
148             ),
149             new ModelCheckboxMenu(
150                 "CONSOLE_JAVA_LABEL",
151                 PreferencesUtil.JAVA_VISIBLE,
152                 () -> MediatorHelper.panelConsoles().insertJavaTab(),
153                 UiUtil.CUP.getIcon(),
154                 false
155             )
156         ).forEach(model -> {
157             var menuItem = new JCheckBoxMenuItem(
158                 I18nUtil.valueByKey(model.i18n),
159                 model.icon,
160                 model.isChecked
161             ) {
162                 @Override
163                 protected void processMouseEvent(MouseEvent e) {
164                     if (RadioItemNonClosing.shouldClose(e, this)) {
165                         super.processMouseEvent(e);
166                     }
167                 }
168             };
169             I18nViewUtil.addComponentForKey(model.i18n, menuItem);
170             menuPanel.add(menuItem);
171 
172             menuItem.addActionListener(actionEvent -> {
173                 if (menuItem.isSelected()) {
174                     model.runnableInsertTab.run();
175                 } else {
176                     MediatorHelper.tabConsoles().remove(MediatorHelper.tabConsoles().indexOfTab(model.icon));
177                 }
178             });
179         });
180 
181         this.add(menuPanel);
182         this.add(new JSeparator());
183         this.add(this.getMenuItemSqlEngine());
184         this.add(this.getMenuItemPreferences());
185     }
186 
187     private JMenuItem getMenuItemSqlEngine() {
188         var itemSqlEngine = new JMenuItem(I18nUtil.valueByKey(MenuWindows.I18N_SQL_ENGINE));
189         I18nViewUtil.addComponentForKey(MenuWindows.I18N_SQL_ENGINE, itemSqlEngine);
190         itemSqlEngine.setName("itemSqlEngine");
191         itemSqlEngine.setMnemonic('S');
192 
193         // Render the SQL Engine dialog behind the scene
194         var titleTabSqlEngine = "SQL Engine";
195 
196         itemSqlEngine.addActionListener(actionEvent -> {
197             for (var i = 0 ; i < MediatorHelper.tabResults().getTabCount() ; i++) {
198                 if (titleTabSqlEngine.equals(MediatorHelper.tabResults().getTitleAt(i))) {
199                     MediatorHelper.tabResults().setSelectedIndex(i);
200                     return;
201                 }
202             }
203 
204             MediatorHelper.frame().getSplitNS().initSplitOrientation();
205 
206             var panelSqlEngine = new SqlEngine();
207             MediatorHelper.tabResults().addTab(titleTabSqlEngine, panelSqlEngine);
208             MediatorHelper.tabResults().setSelectedComponent(panelSqlEngine);  // Focus on the new tab
209 
210             // Create a custom tab header
211             var header = new TabHeader(I18nViewUtil.valueByKey(MenuWindows.I18N_SQL_ENGINE), UiUtil.COG.getIcon());
212             I18nViewUtil.addComponentForKey(MenuWindows.I18N_SQL_ENGINE, header.getTabLabel());
213 
214             // Apply the custom header to the tab
215             MediatorHelper.tabResults().setTabComponentAt(MediatorHelper.tabResults().indexOfComponent(panelSqlEngine), header);
216             MediatorHelper.tabResults().updateUI();  // required: light, open/close prefs, dark => light artifacts
217         });
218 
219         return itemSqlEngine;
220     }
221 
222     private JMenuItem getMenuItemPreferences() {
223         JMenuItem itemPreferences = new JMenuItem(I18nUtil.valueByKey(MenuWindows.I18N_PREFERENCES), 'P');
224         I18nViewUtil.addComponentForKey(MenuWindows.I18N_PREFERENCES, itemPreferences);
225         itemPreferences.setName("itemPreferences");
226 
227         // Render the Preferences dialog behind the scene
228         var titleTabPreferences = "Preferences";
229 
230         itemPreferences.addActionListener(actionEvent -> {
231             for (var i = 0 ; i < MediatorHelper.tabResults().getTabCount() ; i++) {
232                 if (titleTabPreferences.equals(MediatorHelper.tabResults().getTitleAt(i))) {
233                     MediatorHelper.tabResults().setSelectedIndex(i);
234                     return;
235                 }
236             }
237 
238             MediatorHelper.frame().getSplitNS().initSplitOrientation();
239 
240             var panelPreferences = new PanelPreferences();
241             MediatorHelper.tabResults().addTab(titleTabPreferences, panelPreferences);
242             MediatorHelper.tabResults().setSelectedComponent(panelPreferences);  // Focus on the new tab
243 
244             // Create a custom tab header
245             var header = new TabHeader(I18nViewUtil.valueByKey(MenuWindows.I18N_PREFERENCES), UiUtil.COG.getIcon());
246             I18nViewUtil.addComponentForKey(MenuWindows.I18N_PREFERENCES, header.getTabLabel());
247 
248             // Apply the custom header to the tab
249             MediatorHelper.tabResults().setTabComponentAt(MediatorHelper.tabResults().indexOfComponent(panelPreferences), header);
250 
251             MediatorHelper.tabResults().updateUI();  // required: light, open/close prefs, dark => light artifacts
252         });
253 
254         return itemPreferences;
255     }
256 
257     private JMenu initMenuTranslation() {
258         var menuTranslation = new JMenu(I18nUtil.valueByKey("MENUBAR_LANGUAGE"));
259         I18nViewUtil.addComponentForKey("MENUBAR_LANGUAGE", menuTranslation);
260         menuTranslation.setName("menuTranslation");
261         menuTranslation.setMnemonic('L');
262 
263         var groupRadioLanguage = new ButtonGroup();
264         var atomicIsAnySelected = new AtomicBoolean(false);
265         AppMenubar.MODELS_ITEM.forEach(model -> {
266             atomicIsAnySelected.set(atomicIsAnySelected.get() || model.getLanguage().isCurrentLanguage());
267             model.setMenuItem(new RadioItemNonClosing(
268                 model.getLanguage().getMenuItemLabel(),
269                 model.getLanguage().getFlag(),
270                 model.getLanguage().isCurrentLanguage()
271             ));
272             model.getMenuItem().addActionListener(actionEvent -> {
273                 this.appMenubar.switchLocale(
274                     model.getLanguage() == Language.EN
275                     ? Locale.ROOT  // required as no bundle 'en'
276                     : Locale.forLanguageTag(model.getLanguage().getLanguageTag())
277                 );
278                 MediatorHelper.model().getMediatorUtils().getPreferencesUtil().withLanguageTag(model.getLanguage().getLanguageTag()).persist();
279             });
280             menuTranslation.add(model.getMenuItem());
281             groupRadioLanguage.add(model.getMenuItem());
282         });
283 
284         AppMenubar.MODELS_ITEM.stream().filter(model -> model.getLanguage() == Language.EN)
285         .forEach(modelItem -> {
286             modelItem.getMenuItem().setSelected(!atomicIsAnySelected.get());
287             modelItem.getMenuItem().setName("itemEnglish");
288         });
289         AppMenubar.MODELS_ITEM.stream().filter(model -> model.getLanguage() == Language.RU)
290         .forEach(modelItem -> modelItem.getMenuItem().setName("itemRussian"));
291         AppMenubar.MODELS_ITEM.stream().filter(model -> model.getLanguage() == Language.AR)
292         .forEach(modelItem -> modelItem.getMenuItem().setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT));
293 
294         return menuTranslation;
295     }
296 
297     public void switchLocaleFromPreferences() {
298         AppMenubar.MODELS_ITEM.stream().filter(model -> model.getLanguage().getLanguageTag().equals(
299             MediatorHelper.model().getMediatorUtils().getPreferencesUtil().getLanguageTag()
300         ))
301         .forEach(modelItem -> modelItem.getMenuItem().doClick());
302     }
303 
304 
305     // Getter and setter
306 
307     public JMenu getMenuView() {
308         return this.menuView;
309     }
310 }