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 */
021package csheets.ui;
022
023import java.awt.Component;
024import java.awt.event.ActionEvent;
025import java.awt.event.KeyEvent;
026import java.io.File;
027import java.util.Properties;
028
029import javax.swing.AbstractAction;
030import javax.swing.ImageIcon;
031import javax.swing.JMenu;
032import javax.swing.JMenuItem;
033import javax.swing.JSeparator;
034
035import csheets.CleanSheets;
036import csheets.SpreadsheetAppEvent;
037import csheets.SpreadsheetAppListener;
038import csheets.ui.ctrl.OpenAction;
039import 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")
047public 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}