diff --git a/source/options.txt b/source/options.txt index 86ea267..45c674d 100644 --- a/source/options.txt +++ b/source/options.txt @@ -18,6 +18,13 @@ ######################### User Preferences ########################### ############################################################################ +# How many recently opened files should be displayed? +thdl.number.of.recently.opened.files.to.show = 4 + +# How many characters maximum do you want to show on the File menu for +# a recently opened file? +thdl.max.chars.in.recently.opened.file.name = 40 + # Set this to the full path of the user preferences file, or to the # empty string if you wish to use the default values, which are # system-specific. diff --git a/source/org/thdl/tib/input/Jskad.java b/source/org/thdl/tib/input/Jskad.java index 047694d..83389ed 100644 --- a/source/org/thdl/tib/input/Jskad.java +++ b/source/org/thdl/tib/input/Jskad.java @@ -126,25 +126,10 @@ public class Jskad extends JPanel implements DocumentListener { /** Do not use this JPanel constructor. */ private Jskad(LayoutManager lm, boolean isDB) { super(lm, isDB); } - /** Tells ThdlOptions about the recently opened files. */ - // DLC FIXME: this doesn't keep the second-most recently opened item second if you have just three items. - private static void storeRecentlyOpenedFilePreferences() { - int N = ThdlOptions.getIntegerOption("thdl.number.of.recently.opened.files.to.show", 4); - int n = 0; - // We store 2*N files in the preferences in case some are deleted. - for (int k = recentlyOpenedFiles.size(); k > 0 && n < 2*N; k--) { - File f = (File)recentlyOpenedFiles.elementAt(k - 1); - if (f.isFile()) { - ThdlOptions.setUserPreference("thdl.recently.opened.file." + n++, - f.getAbsolutePath()); - } - } - } - /** Saves user preferences to disk if possible. */ private void savePreferencesAction() { try { - storeRecentlyOpenedFilePreferences(); + RecentlyOpenedFilesDatabase.storeRecentlyOpenedFilePreferences(); if (!ThdlOptions.saveUserPreferences()) { JOptionPane.showMessageDialog(Jskad.this, @@ -178,43 +163,14 @@ public class Jskad extends JPanel implements DocumentListener { /** pane displaying Jskad's single HTML help file */ private static HTMLPane helpPane; - /** Returns the nth most recently opened file, given - that we care about N recently opened files in - total. When n is zero, the most recently opened - file is returned. This file does exist. Returns null if we - haven't kept track of enough files to say. */ - private static File getNthRecentlyOpenedFile(int n, int N) { - for (int i = n; i < N*2; i++) { - String x = ThdlOptions.getStringOption("thdl.recently.opened.file." + i); - if (null == x) - return null; - File f = new File(x); - if (f.isFile()) - return f; - } - return null; - } - - /** a vector with the most recently opened file at its end. */ - private static Vector recentlyOpenedFiles = new Vector(); /** the File menu */ private JMenu fileMenu = null; - private static void addMostRecentlyOpenedFile(File fileChosen) { - // the last element is the most recently opened. - int index = recentlyOpenedFiles.indexOf(fileChosen); - if (index > -1) { - recentlyOpenedFiles.remove(index); - } - recentlyOpenedFiles.add(fileChosen); - } - /** Updates state information now that we know that fileChosen is the most recently opened file. */ private static void noteMostRecentlyOpenedFile(File fileChosen) { - addMostRecentlyOpenedFile(fileChosen); - storeRecentlyOpenedFilePreferences(); - + RecentlyOpenedFilesDatabase.setMostRecentlyOpenedFile(fileChosen); + int i, sz = jskads.size(); for (i = 0; i < sz; i++) { ((Jskad)jskads.elementAt(i)).updateRecentlyOpenedFilesMenuItems(); @@ -225,21 +181,20 @@ public class Jskad extends JPanel implements DocumentListener { int ic = fileMenu.getItemCount(); while (fileMenu.getItemCount() > 8) fileMenu.remove(7); - int N = ThdlOptions.getIntegerOption("thdl.number.of.recently.opened.files.to.show", 4); + int N = RecentlyOpenedFilesDatabase.getNumberOfFilesToShow(); // Avoid adding duplicate entries: - int maximum = recentlyOpenedFiles.size(); - if (N > maximum) N = maximum; boolean addedSeparator = false; for (int i = 0; i < N; i++) { final File recentlyOpenedFile - = getNthRecentlyOpenedFile(N-i-1, N); + = RecentlyOpenedFilesDatabase.getNthRecentlyOpenedFile(N-i-1); if (null != recentlyOpenedFile) { if (!addedSeparator) { fileMenu.insertSeparator(6); addedSeparator = true; } - JMenuItem item = new JMenuItem((N-i) + " " + recentlyOpenedFile.getAbsolutePath()); + JMenuItem item = new JMenuItem((N-i) + " " + + RecentlyOpenedFilesDatabase.getLabel(recentlyOpenedFile)); item.addActionListener(new ThdlActionListener() { public void theRealActionPerformed(ActionEvent e) { openFile(recentlyOpenedFile); @@ -274,7 +229,11 @@ public class Jskad extends JPanel implements DocumentListener { = ThdlOptions.getStringOption("thdl.Jskad.working.directory", null); fileChooser - = new JFileChooser(whereToStart.equals("") ? null : whereToStart); + = new JFileChooser((whereToStart == null) + ? null + : (whereToStart.equals("") + ? null + : whereToStart)); rtfFilter = new RTFFilter(); txtFilter = new TXTFilter(); fileChooser.addChoosableFileFilter(rtfFilter); @@ -339,14 +298,6 @@ public class Jskad extends JPanel implements DocumentListener { }); fileMenu.add(saveAsItem); - // Add the N most recently opened files. - int N = ThdlOptions.getIntegerOption("thdl.number.of.recently.opened.files.to.show", 4); - int maxCharsToShow - = ThdlOptions.getIntegerOption("thdl.max.chars.in.recently.opened.file.name", 35); - for (int i = 0; i < 2*N; i++) { - addMostRecentlyOpenedFile(getNthRecentlyOpenedFile(i, N)); - } - if (parentObject instanceof JFrame) { JMenuItem exitItem = new JMenuItem("Exit"); exitItem.addActionListener(new ThdlActionListener() { @@ -941,8 +892,12 @@ public class Jskad extends JPanel implements DocumentListener { String whereToStart = ThdlOptions.getStringOption("thdl.Jskad.working.directory", null); - fileChooser - = new JFileChooser(whereToStart.equals("") ? null : whereToStart); + fileChooser + = new JFileChooser((whereToStart == null) + ? null + : (whereToStart.equals("") + ? null + : whereToStart)); fileChooser.addChoosableFileFilter(rtfFilter); setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); diff --git a/source/org/thdl/tib/input/RecentlyOpenedFilesDatabase.java b/source/org/thdl/tib/input/RecentlyOpenedFilesDatabase.java new file mode 100644 index 0000000..07ae259 --- /dev/null +++ b/source/org/thdl/tib/input/RecentlyOpenedFilesDatabase.java @@ -0,0 +1,155 @@ +/* +The contents of this file are subject to the THDL Open Community License +Version 1.0 (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License on the THDL web site +(http://www.thdl.org/). + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +License for the specific terms governing rights and limitations under the +License. + +The Initial Developer of this software is the Tibetan and Himalayan Digital +Library (THDL). Portions created by the THDL are Copyright 2003 THDL. +All Rights Reserved. + +Contributor(s): ______________________________________. +*/ + +package org.thdl.tib.input; + +import org.thdl.util.ThdlOptions; + +import java.util.Vector; +import java.io.File; + +/** A database of the files most recently opened in Jskad. The client + must call {@link #storeRecentlyOpenedFilePreferences()} before + exiting. + @author David Chandler +*/ +class RecentlyOpenedFilesDatabase { + + /** Tells ThdlOptions about the recently opened files. Call this before program exit. */ + // DLC FIXME: this doesn't keep the second-most recently opened item second if you have just three items. + public static void storeRecentlyOpenedFilePreferences() { + int n = 0; + // We store 2*N files in the preferences in case some are deleted. + for (int k = 0; k < recentlyOpenedFiles.size() && n < 2*N; k++) { + File f = (File)recentlyOpenedFiles.elementAt(k); + if (f.isFile()) { + ThdlOptions.setUserPreference("thdl.recently.opened.file." + n++, + f.getAbsolutePath()); + } + } + } + + /** the number of recently opened files to display on the File + menu */ + private static int N; + + /** the maximum number of characters to display in a label shown + to the user for a recently opened file */ + private static int maxCharsToShow; + + /** Returns the maximum number of recently opened files that the + user wishes to see in the File menu. */ + public static int getNumberOfFilesToShow() { + if (uninitialized) init(); + return N; + } + + /** false iff we are in the process of initializing or have + already initialized */ + private static boolean uninitialized = true; + + /** Reads the user's preferences and fills in {@link + #recentlyOpenedFiles} based on them. */ + private static void init() { + if (uninitialized) { + uninitialized = false; // must come first! + + maxCharsToShow + = ThdlOptions.getIntegerOption("thdl.max.chars.in.recently.opened.file.name", 40); + N = ThdlOptions.getIntegerOption("thdl.number.of.recently.opened.files.to.show", 4); + + // Add the N most recently opened files. + + if (maxCharsToShow < 5) + maxCharsToShow = 5; + for (int i = 2*N - 1; i >= 0; i--) { + setMostRecentlyOpenedFile(getNthRecentlyOpenedFileFromPreferences(i)); + } + } + } + + /** Returns the nth most recently opened file, given + that we care about N recently opened files in + total. When n is zero, the most recently opened + file is returned. This file does exist. Returns null if we + haven't kept track of enough files to say. */ + public static File getNthRecentlyOpenedFile(int n) { + if (uninitialized) init(); + while (n < recentlyOpenedFiles.size()) { + File f = (File)recentlyOpenedFiles.elementAt(n); + if (f.isFile()) + return f; + ++n; + } + return null; + } + + /** Returns the nth most recently opened, existing file listed in + the user's preferences. Returns null if their preferences do + not contain n+1 existing files. */ + private static File getNthRecentlyOpenedFileFromPreferences(int n) { + if (uninitialized) init(); + for (int i = n; i < N*2; i++) { + String x = ThdlOptions.getStringOption("thdl.recently.opened.file." + i); + if (null == x) + return null; + File f = new File(x); + if (f.isFile()) + return f; + } + return null; + } + + /** Notes the fact that fileChosen was the file most recently + opened by Jskad. */ + public static void setMostRecentlyOpenedFile(File fileChosen) { + if (null != fileChosen) { + if (uninitialized) init(); + // the first element is the most recently opened. + int index = recentlyOpenedFiles.indexOf(fileChosen); + if (index > -1) { + recentlyOpenedFiles.remove(index); + } + recentlyOpenedFiles.add(0, fileChosen); + } + } + + /** a vector with the most recently opened file at its + beginning, the least recently opened at its end. */ + private static Vector recentlyOpenedFiles = new Vector(); + + /** Prints debugging information to System.err. */ + public static void printDebuggingInfo() { + if (uninitialized) init(); + System.err.println(""); + for (int i = 0; i < recentlyOpenedFiles.size(); i++) + System.err.println("File " + i + " (where 0 is most recent) is " + ((File)recentlyOpenedFiles.elementAt(i)).getAbsolutePath()); + System.err.println(""); + } + + /** Returns a user-friendly label for f that is not longer than + the user's preferences allow. */ + public static String getLabel(File f) { + if (uninitialized) init(); + String path = f.getAbsolutePath(); + int l; + if ((l = path.length()) <= maxCharsToShow) + return path; + return "..." + path.substring(l - maxCharsToShow + 3); + } +}