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.list;
12  
13  import com.formdev.flatlaf.util.SystemFileChooser;
14  import com.jsql.util.I18nUtil;
15  import com.jsql.util.LogLevelUtil;
16  import com.jsql.view.swing.util.I18nViewUtil;
17  import com.jsql.view.swing.util.MediatorHelper;
18  import org.apache.logging.log4j.LogManager;
19  import org.apache.logging.log4j.Logger;
20  
21  import javax.swing.*;
22  import java.awt.*;
23  import java.awt.event.*;
24  import java.io.IOException;
25  import java.util.AbstractMap.SimpleEntry;
26  import java.util.Arrays;
27  import java.util.stream.Stream;
28  
29  /**
30   * A Mouse action to display a popupmenu on a JList.
31   */
32  public class MouseAdapterMenuAction extends MouseAdapter {
33      
34      private static final Logger LOGGER = LogManager.getRootLogger();
35      
36      /**
37       * JList to add popupmenu.
38       */
39      private final DnDList dndList;
40      
41      /**
42       * Create a popup menu for current JList item.
43       * @param dndList List with action
44       */
45      public MouseAdapterMenuAction(DnDList dndList) {
46          this.dndList = dndList;
47      }
48      
49      /**
50       * Displays a popup menu for JList.
51       * @param mouseEvent Mouse event
52       */
53      @SuppressWarnings("unchecked")
54      public void showPopup(final MouseEvent mouseEvent) {
55          if (mouseEvent.isPopupTrigger()) {
56              JList<ItemList> list = (JList<ItemList>) mouseEvent.getSource();
57  
58              JPopupMenu popupMenuList = this.initMenu(mouseEvent);
59              popupMenuList.applyComponentOrientation(ComponentOrientation.getOrientation(I18nUtil.getCurrentLocale()));
60              // Fix #26274: IllegalComponentStateException on show()
61              try {
62                  popupMenuList.show(
63                      list,
64                      ComponentOrientation.RIGHT_TO_LEFT.equals(ComponentOrientation.getOrientation(I18nUtil.getCurrentLocale()))
65                      ? mouseEvent.getX() - popupMenuList.getWidth()
66                      : mouseEvent.getX(),
67                      mouseEvent.getY()
68                  );
69              } catch (IllegalComponentStateException e) {
70                  LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
71              }
72              
73              popupMenuList.setLocation(
74                  ComponentOrientation.RIGHT_TO_LEFT.equals(ComponentOrientation.getOrientation(I18nUtil.getCurrentLocale()))
75                  ? mouseEvent.getXOnScreen() - popupMenuList.getWidth()
76                  : mouseEvent.getXOnScreen(),
77                  mouseEvent.getYOnScreen()
78              );
79          }
80      }
81  
82      private JPopupMenu initMenu(final MouseEvent mouseEvent) {
83          var popupMenuList = new JPopupMenu();
84          
85          boolean isNonUbuntu = I18nViewUtil.isNonUbuntu(I18nUtil.getCurrentLocale());
86          
87          JMenuItem menuImport = new JMenuItem();
88          JMenuItem menuExport = new JMenuItem();
89          JMenuItem menuCut = new JMenuItem();
90          JMenuItem menuCopy = new JMenuItem();
91          JMenuItem menuPaste = new JMenuItem();
92          JMenuItem menuDelete = new JMenuItem();
93          JMenuItem menuNew = new JMenuItem();
94          JMenuItem menuRestoreDefault = new JMenuItem();
95          JMenuItem menuSelectAll = new JMenuItem();
96          
97          Stream.of(
98              new SimpleEntry<>(menuImport, "LIST_IMPORT_CONFIRM_TITLE"),
99              new SimpleEntry<>(menuExport, "LIST_EXPORT_TITLE"),
100             new SimpleEntry<>(menuCut, "LIST_CUT"),
101             new SimpleEntry<>(menuCopy, "CONTEXT_MENU_COPY"),
102             new SimpleEntry<>(menuPaste, "LIST_PASTE"),
103             new SimpleEntry<>(menuDelete, "LIST_DELETE"),
104             new SimpleEntry<>(menuNew, "LIST_NEW_VALUE"),
105             new SimpleEntry<>(menuRestoreDefault, "LIST_RESTORE_DEFAULT"),
106             new SimpleEntry<>(menuSelectAll, "CONTEXT_MENU_SELECT_ALL")
107         )
108         .forEach(entry -> {
109             entry.getKey().setText(
110                 isNonUbuntu
111                 ? I18nViewUtil.valueByKey(entry.getValue())
112                 : I18nUtil.valueByKey(entry.getValue())
113             );
114             entry.getKey().setName(entry.getValue());
115             I18nViewUtil.addComponentForKey(entry.getValue(), entry.getKey());
116         });
117 
118         menuCut.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, InputEvent.CTRL_DOWN_MASK));
119         menuCopy.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_DOWN_MASK));
120         menuPaste.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_DOWN_MASK));
121         menuSelectAll.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_DOWN_MASK));
122         
123         final var importFileDialog = new SystemFileChooser(MediatorHelper.model().getMediatorUtils().preferencesUtil().getPathFile());
124         importFileDialog.setDialogTitle(I18nUtil.valueByKey("LIST_IMPORT_CONFIRM_TITLE"));
125         importFileDialog.setMultiSelectionEnabled(true);
126 
127         menuNew.addActionListener(new MenuActionNewValue(this.dndList));
128 
129         menuImport.addActionListener(actionEvent -> {
130             var choice = 0;
131             // Fix #1896: NullPointerException on showOpenDialog()
132             // Fix #42831: ClassCastException on showOpenDialog()
133             try {
134                 choice = importFileDialog.showOpenDialog(this.dndList.getTopLevelAncestor());
135                 if (choice == JFileChooser.APPROVE_OPTION) {
136                     this.dndList.dropPasteFile(
137                         Arrays.asList(importFileDialog.getSelectedFiles()),
138                         this.dndList.locationToIndex(mouseEvent.getPoint())
139                     );
140                 }
141             } catch (ClassCastException | NullPointerException | IOException e) {
142                 LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
143             }
144         });
145 
146         menuCopy.addActionListener(actionEvent -> {
147             var action = this.dndList.getActionMap().get(TransferHandler.getCopyAction().getValue(Action.NAME));
148             if (action != null) {
149                 action.actionPerformed(
150                     new ActionEvent(this.dndList, ActionEvent.ACTION_PERFORMED, null)
151                 );
152             }
153         });
154 
155         menuCut.addActionListener(actionEvent -> {
156             var action = this.dndList.getActionMap().get(TransferHandler.getCutAction().getValue(Action.NAME));
157             if (action != null) {
158                 action.actionPerformed(
159                     new ActionEvent(this.dndList, ActionEvent.ACTION_PERFORMED, null)
160                 );
161             }
162         });
163 
164         menuPaste.addActionListener(actionEvent -> {
165             var action = this.dndList.getActionMap().get(TransferHandler.getPasteAction().getValue(Action.NAME));
166             if (action != null) {
167                 action.actionPerformed(
168                     new ActionEvent(this.dndList, ActionEvent.ACTION_PERFORMED, null)
169                 );
170             }
171         });
172 
173         menuDelete.addActionListener(actionEvent -> this.dndList.removeSelectedItem());
174         menuExport.addActionListener(new MenuActionExport(this.dndList));
175         menuExport.setEnabled(!this.dndList.listModel.isEmpty());
176         menuRestoreDefault.addActionListener(actionEvent -> this.dndList.restore());
177         menuSelectAll.addActionListener(actionEvent -> {
178             var start = 0;
179             int end = this.dndList.getModel().getSize() - 1;
180             if (end >= 0) {
181                 this.dndList.setSelectionInterval(start, end);
182             }
183         });
184 
185         popupMenuList.add(menuNew);
186         popupMenuList.add(new JSeparator());
187         popupMenuList.add(menuCut);
188         popupMenuList.add(menuCopy);
189         popupMenuList.add(menuPaste);
190         popupMenuList.add(menuDelete);
191         popupMenuList.add(new JSeparator());
192         popupMenuList.add(menuSelectAll);
193         popupMenuList.add(new JSeparator());
194         popupMenuList.add(menuImport);
195         popupMenuList.add(menuExport);
196         popupMenuList.add(new JSeparator());
197         popupMenuList.add(menuRestoreDefault);
198         return popupMenuList;
199     }
200 
201     @Override
202     public void mousePressed(MouseEvent e) {
203         if (SwingUtilities.isRightMouseButton(e)) {
204             int clickIndex = this.dndList.locationToIndex(e.getPoint());
205             var containsIndex = false;
206             for (int currentIndex: this.dndList.getSelectedIndices()) {
207                 if (currentIndex == clickIndex) {
208                     containsIndex = true;
209                     break;
210                 }
211             }
212             if (!containsIndex) {
213                 this.dndList.setSelectedIndex(clickIndex);
214             }
215         }
216         this.showPopup(e);
217     }
218 
219     @Override
220     public void mouseReleased(MouseEvent e) {
221         this.showPopup(e);
222     }
223 }