001 /*
002 * Copyright (c) 2005 Einar Pehrson <einar@pehrson.nu>.
003 *
004 * This file is part of
005 * CleanSheets - a spreadsheet application for the Java platform.
006 *
007 * CleanSheets is free software; you can redistribute it and/or modify
008 * it under the terms of the GNU General Public License as published by
009 * the Free Software Foundation; either version 2 of the License, or
010 * (at your option) any later version.
011 *
012 * CleanSheets is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015 * GNU General Public License for more details.
016 *
017 * You should have received a copy of the GNU General Public License
018 * along with CleanSheets; if not, write to the Free Software
019 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020 */
021 package csheets.ui;
022
023 import java.awt.Component;
024 import java.awt.event.ActionEvent;
025 import java.awt.event.KeyEvent;
026 import java.io.File;
027 import java.util.Properties;
028
029 import javax.swing.AbstractAction;
030 import javax.swing.ImageIcon;
031 import javax.swing.JMenu;
032 import javax.swing.JMenuItem;
033 import javax.swing.JSeparator;
034
035 import csheets.CleanSheets;
036 import csheets.SpreadsheetAppEvent;
037 import csheets.SpreadsheetAppListener;
038 import csheets.ui.ctrl.OpenAction;
039 import csheets.ui.ctrl.UIController;
040
041 /**
042 * A menu for displaying recently opened files, and allowing the user to
043 * reopen them.
044 * @author Einar Pehrson
045 */
046 @SuppressWarnings("serial")
047 public class ReopenMenu extends JMenu implements SpreadsheetAppListener {
048
049 /** The CleanSheets application */
050 private CleanSheets app;
051
052 /** The maximum number of items in the menu */
053 private int maximumItems = 10;
054
055 /** The application properties */
056 private Properties props;
057
058 /** The number of menu items on the menu not denoting files */
059 private int nonReopenItems;
060
061 /** The user interface controller */
062 private UIController uiController;
063
064 /**
065 * Creates a reopen menu, and creates items using the given properties (if available).
066 * @param app the CleanSheets application
067 * @param uiController the user interface controller
068 */
069 public ReopenMenu(CleanSheets app, UIController uiController) {
070 super("Reopen");
071
072 // Stores members
073 this.app = app;
074 this.uiController = uiController;
075 this.props = app.getUserProperties();
076
077 // Configures menu
078 app.addSpreadsheetAppListener(this);
079 setMnemonic(KeyEvent.VK_R);
080 setIcon(new ImageIcon(CleanSheets.class.getResource("res/img/reopen.gif")));
081
082 if (props != null) {
083 // Loads recent files from properties and adds menu items
084 String filename;
085 for (int i = 0; (filename = props.getProperty("recentfile" + i)) != null; i++) {
086 File file = new File(filename);
087 if (file.exists()) addReopenItem(file, false);
088 }
089 }
090
091 // Adds removal items
092 addSeparator();
093 add(new RemoveObsoleteAction());
094 add(new RemoveAllAction());
095 nonReopenItems = 3;
096 }
097
098 /**
099 * Adds an item for this file to the top of the reopen menu.
100 * @param file the filename of the file
101 * @return the item that was added
102 */
103 public JMenuItem addReopenItem(File file) {
104 return addReopenItem(file, true);
105 }
106
107 /**
108 * Adds an item for this file to the top of the reopen menu.
109 * @param file the filename of the file
110 * @param updateProperties whether to update properties after adding the iten
111 * @return the item that was added
112 */
113 private JMenuItem addReopenItem(File file, boolean updateProperties) {
114 // Removes any existing identical items
115 Component[] items = getMenuComponents();
116 for (int i = 0; i < items.length; i++) {
117 // Breaks at separator
118 if (items[i] instanceof JSeparator) break;
119
120 // Removes item, if identical
121 JMenuItem item = (JMenuItem)items[i];
122 if (file.getAbsolutePath().equals(item.getText()))
123 remove(item);
124 }
125
126 // Adds the item to the menu and trims the menu to the appropriate size
127 JMenuItem item = insert(new ReopenAction(app, uiController, file), 0);
128 while (getMenuComponentCount() - nonReopenItems > maximumItems) remove(maximumItems);
129
130 // Updates properties
131 if (updateProperties)
132 updateProperties();
133
134 return item;
135 }
136
137 /**
138 * Removes all obsolete file items from the reopen menu,
139 * i.e. the items referring to files that don't exist.
140 */
141 public void removeObsolete() {
142 Component[] items = getMenuComponents();
143 for (int i = 0; i < items.length; i++) {
144 // Breaks at separator
145 if (items[i] instanceof JSeparator) break;
146
147 // Removes item, if obsolete
148 JMenuItem item = (JMenuItem)items[i];
149 if (!new File(item.getText()).exists())
150 remove(item);
151 }
152
153 // Updates properties
154 updateProperties();
155 }
156
157 /**
158 * Removes all file items from the reopen menu,
159 */
160 public void removeAll() {
161 Component[] items = getMenuComponents();
162 for (int i = 0; i < items.length; i++) {
163 // Breaks at separator
164 if (items[i] instanceof JSeparator) break;
165
166 // Removes item
167 remove(items[i]);
168 }
169
170 // Updates properties
171 updateProperties();
172 }
173
174 /**
175 * Sets the maximum number of reopen items in the menu.
176 * @param items the number of reopen items in the menu
177 */
178 public void setMaximumItems(int items) {
179 this.maximumItems = items;
180 }
181
182 /**
183 * Updates the recent files in the application properties.
184 */
185 private void updateProperties() {
186 if (props != null) {
187 // Stores the current recent files
188 int i = 0;
189 for (int n = getMenuComponentCount() - nonReopenItems; i < n; i++)
190 props.setProperty("recentfile" + (n - i - 1),
191 ((JMenuItem)getMenuComponent(i)).getText());
192
193 for (; (props.getProperty("recentfile" + i)) != null; i++)
194 props.remove("recentfile" + i);
195 }
196 }
197
198 public void workbookCreated(SpreadsheetAppEvent event) {}
199
200 public void workbookLoaded(SpreadsheetAppEvent event) {
201 addReopenItem(event.getFile(), true);
202 }
203
204 public void workbookUnloaded(SpreadsheetAppEvent event) {}
205
206 public void workbookSaved(SpreadsheetAppEvent event) {
207 addReopenItem(event.getFile(), true);
208 }
209
210 /**
211 * An action for reopening a spreadsheet.
212 * @author Einar Pehrson
213 */
214 @SuppressWarnings("serial")
215 protected static class ReopenAction extends OpenAction {
216
217 /** The file to open */
218 private File file;
219
220 /**
221 * Creates a new reopen action.
222 * @param app the CleanSheets application
223 * @param uiController the user interface controller
224 * @param file the file to open
225 */
226 public ReopenAction(CleanSheets app, UIController uiController,
227 File file) {
228 super(app, uiController, null);
229 this.file = file;
230 setEnabled(true);
231 putValue(NAME, file.getAbsolutePath());
232 putValue(ACTION_COMMAND_KEY, file.getAbsolutePath());
233 }
234
235 protected void defineProperties() {
236 putValue(SHORT_DESCRIPTION, null);
237 putValue(SMALL_ICON, null);
238 }
239
240 public File getFile() {
241 return file;
242 }
243 }
244
245 /**
246 * An action for removing the obsolete items from the menu.
247 */
248 @SuppressWarnings("serial")
249 protected class RemoveObsoleteAction extends AbstractAction {
250
251 /**
252 * Creates a new remove obsolete action.
253 */
254 public RemoveObsoleteAction() {
255 // Configures action
256 String name = "Remove obsolete";
257 putValue(NAME, name);
258 putValue(SHORT_DESCRIPTION, name);
259 putValue(ACTION_COMMAND_KEY, name);
260 putValue(MNEMONIC_KEY, KeyEvent.VK_O);
261 }
262
263 public void actionPerformed(ActionEvent e) {
264 removeObsolete();
265 }
266 }
267
268 /**
269 * An action for removing all items from the menu.
270 */
271 @SuppressWarnings("serial")
272 protected class RemoveAllAction extends AbstractAction {
273
274 /**
275 * Creates a new remove all action.
276 */
277 public RemoveAllAction() {
278 // Configures action
279 String name = "Remove all";
280 putValue(NAME, name);
281 putValue(SHORT_DESCRIPTION, name);
282 putValue(ACTION_COMMAND_KEY, name);
283 putValue(MNEMONIC_KEY, KeyEvent.VK_R);
284 }
285
286 public void actionPerformed(ActionEvent e) {
287 removeAll();
288 }
289 }
290 }