Added license and copyright notice.
[instantlauncher.git] / src / main / java / eu / svjatoslav / instantlauncher / menu / Menu.java
1 /*
2  * Instantlauncher. Copyright ©2012-2019, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of version 3 of the GNU Lesser General Public License
6  * or later as published by the Free Software Foundation.
7  */
8
9 package eu.svjatoslav.instantlauncher.menu;
10
11 import eu.svjatoslav.commons.gui.dialog.ExceptionDialog;
12 import eu.svjatoslav.instantlauncher.Utils;
13
14 import javax.swing.*;
15 import javax.swing.border.EmptyBorder;
16 import java.awt.*;
17 import java.awt.event.MouseEvent;
18 import java.awt.event.MouseListener;
19 import java.io.File;
20 import java.util.Arrays;
21 import java.util.HashSet;
22 import java.util.Set;
23
24 class Menu {
25
26     public static final Dimension SIZE_MENU_PANEL = new Dimension(210, 900);
27     private static final Color BUTTON_BG_DIRECTORY = new Color(0, 0, 60);
28
29     private static final Color BUTTON_BG_DIRECTORY_SELECTED = new Color(0, 0, 100);
30     private static final Color BUTTON_BG_LINK = new Color(60, 60, 0);
31
32     private static final Color BUTTON_BG_LINK_SELECTED = new Color(100, 100, 0);
33     private static final Color BUTTON_BG_EXECUTABLE_FILE = new Color(60, 0, 0);
34
35     private static final Color BUTTON_BG_EXECUTABLE_FILE_SELECTED = new Color(100, 0, 0);
36     private static final Color BUTTON_BG_NONEXECUTABLE_FILE = new Color(0, 60, 0);
37
38     private static final Color BUTTON_BG_NONEXECUTABLE_FILE_SELECTED = new Color(0, 100, 0);
39     private static final Dimension SIZE_BUTTON = new Dimension(200, 30);
40     private final Set<MenuListener> menuListeners = new HashSet<>();
41     private JPanel menuPanel;
42     private JButton selectedButton;
43     private File selectedFile;
44
45     public Menu(final File directory) {
46         buildContentPanel(directory);
47     }
48
49     /**
50      * <pre>
51      * Assign specific colors to:
52      *    Directory
53      *    Executable file
54      *    Non executable file
55      * </pre>
56      */
57     private static Color getFileColor(final File file, final boolean selected) {
58         if (file.isDirectory())
59             if (selected)
60                 return BUTTON_BG_DIRECTORY_SELECTED;
61             else
62                 return BUTTON_BG_DIRECTORY;
63
64         if (file.isFile())
65             if (file.canExecute()) {
66                 if (selected)
67                     return BUTTON_BG_EXECUTABLE_FILE_SELECTED;
68                 else
69                     return BUTTON_BG_EXECUTABLE_FILE;
70             } else if (selected)
71                 return BUTTON_BG_NONEXECUTABLE_FILE_SELECTED;
72             else
73                 return BUTTON_BG_NONEXECUTABLE_FILE;
74
75         if (selected)
76             return BUTTON_BG_LINK_SELECTED;
77         else
78             return BUTTON_BG_LINK;
79     }
80
81     public void addMenuListener(final MenuListener menuListener) {
82         menuListeners.add(menuListener);
83     }
84
85     private JButton buildButton(final File file) {
86         JButton button;
87         if (file.isDirectory())
88             button = new JButton("[ " + file.getName() + " ]");
89         else
90             button = new JButton(file.getName());
91
92         button.setBackground(getFileColor(file, false));
93         button.setForeground(new Color(255, 255, 255));
94
95         button.setBorder(new EmptyBorder(5, 10, 5, 10));
96
97         final Menu.ButtonMouseListener mouseListener = new ButtonMouseListener(button, this, file);
98         button.addMouseListener(mouseListener);
99
100         button.setHorizontalTextPosition(SwingConstants.CENTER);
101
102         // set button size
103         Utils.setComponentSize(button, SIZE_BUTTON);
104
105         return button;
106     }
107
108     private void buildContentPanel(final File directory) {
109         menuPanel = new JPanel();
110
111         Utils.setComponentSize(menuPanel, SIZE_MENU_PANEL);
112
113         final BoxLayout layout = new BoxLayout(menuPanel, BoxLayout.Y_AXIS);
114         menuPanel.setLayout(layout);
115         menuPanel.setBackground(Color.BLACK);
116
117         final File[] listFiles = directory.listFiles();
118
119         // Sort files by name
120         Arrays.sort(listFiles, (f1, f2) -> {
121
122             // directories come before files
123             if (f1.isFile() && f2.isDirectory())
124                 return 1;
125             if (f2.isFile() && f1.isDirectory())
126                 return -1;
127
128             // order by file name
129             return f1.getName().toUpperCase().compareTo(f2.getName().toUpperCase());
130         });
131
132         // add files to the panel
133         for (final File file : listFiles)
134             // ignore hidden files
135             if (!file.getName().startsWith(".")) {
136                 menuPanel.add(Box.createVerticalStrut(10));
137                 menuPanel.add(buildButton(file));
138             }
139     }
140
141     public JPanel getMenuPanel() {
142         return menuPanel;
143     }
144
145     private JButton getSelectedButton() {
146         return selectedButton;
147     }
148
149     private File getSelectedFile() {
150         return selectedFile;
151     }
152
153     private void menuItemHighlighted(final File chosenFile) {
154         for (final MenuListener menuListener : menuListeners)
155             menuListener.menuItemHighlighted(this, chosenFile);
156     }
157
158     private void menuItemSelected(final File chosenFile) {
159         for (final MenuListener menuListener : menuListeners)
160             menuListener.menuItemSelected(chosenFile);
161     }
162
163     private void menuItemSelectedAlternative(final File chosenFile) {
164         for (final MenuListener menuListener : menuListeners)
165             menuListener.menuItemSelectedAlternative(chosenFile);
166     }
167
168     private void menuItemSelectedAlternative2(final File chosenFile) {
169         for (final MenuListener menuListener : menuListeners)
170             menuListener.menuItemSelectedAlternative2(chosenFile);
171     }
172
173     private void setSelected(final JButton selectedButton, final File selectedFile) {
174         this.selectedButton = selectedButton;
175         this.selectedFile = selectedFile;
176     }
177
178     public static class ButtonMouseListener implements MouseListener {
179
180         final JButton parentButton;
181         final Menu parentMenu;
182         final File file;
183
184         public ButtonMouseListener(final JButton button, final Menu parentMenu, final File file) {
185             parentButton = button;
186             this.parentMenu = parentMenu;
187             this.file = file;
188         }
189
190         @Override
191         public void mouseClicked(final MouseEvent e) {
192             try {
193
194                 if (e.getButton() == 1) // left mouse click
195                     parentMenu.menuItemSelected(file);
196                 if (e.getButton() == 2) // middle mouse click
197                     parentMenu.menuItemSelectedAlternative2(file);
198                 if (e.getButton() == 3) //right mouse click
199                     parentMenu.menuItemSelectedAlternative(file);
200             } catch (final Exception ex) {
201                 new ExceptionDialog(ex);
202             }
203         }
204
205         @Override
206         public void mouseEntered(final MouseEvent e) {
207             try {
208                 if (parentButton != parentMenu.getSelectedButton()) {
209
210                     if (parentMenu.getSelectedButton() != null)
211                         parentMenu.getSelectedButton().setBackground(getFileColor(parentMenu.getSelectedFile(), false));
212
213                     parentMenu.setSelected(parentButton, file);
214                     parentButton.setBackground(getFileColor(file, true));
215
216                     parentMenu.menuItemHighlighted(file);
217                 }
218             } catch (final Exception ex) {
219                 new ExceptionDialog(ex);
220             }
221         }
222
223         @Override
224         public void mouseExited(final MouseEvent e) {
225         }
226
227         @Override
228         public void mousePressed(final MouseEvent e) {
229         }
230
231         @Override
232         public void mouseReleased(final MouseEvent e) {
233         }
234
235     }
236
237 }