View Javadoc
1 package com.bonevich.util; 2 3 import java.awt.*; 4 import java.awt.event.ActionEvent; 5 import java.awt.event.ActionListener; 6 import java.awt.event.MouseEvent; 7 import java.io.*; 8 import java.util.ArrayList; 9 10 import javax.swing.*; 11 12 /*** 13 * Provide a file history mechanism for the File menu of a parent frame. 14 * <p> 15 * Borrowed heavily from: 16 * <a href="http://www.javaworld.com/javaworld/javatips/jw-javatip119.html">Java 17 * Tip 119: Don't know much about file history?"</a>, 18 * by Klaus Berg, JavaWorld, 2 November 2001 19 * 20 * @author Jeffrey Bonevich 21 * @since JDK 1.2 22 */ 23 public final class FileHistory 24 { 25 ////////////////////////////////////////////////////////// 26 // Constants 27 private static final int MAX_ITEM_LEN = 50; 28 private static final String FILE_SEPARATOR_STR = System.getProperty("file.separator"); 29 30 ////////////////////////////////////////////////////////// 31 // Attributes 32 private static String _historyFile; 33 private static int _maxItemnames; 34 private static ArrayList _itemnameHistory = new ArrayList(_maxItemnames); 35 private static ArrayList _pathnameHistory = new ArrayList(_maxItemnames); 36 37 private FileHistoryOwner _owner; 38 private JMenu _fileMenu; 39 40 ////////////////////////////////////////////////////////// 41 // Constructors 42 public FileHistory(FileHistoryOwner owner) 43 { 44 _owner = owner; 45 _historyFile = 46 System.getProperty("user.home") 47 + FILE_SEPARATOR_STR 48 + "." + _owner.getApplicationName() 49 + ".cfg"; 50 51 String maxItemnamesStr = System.getProperty("itemnames.max", "9"); 52 try 53 { 54 _maxItemnames = Integer.parseInt(maxItemnamesStr); 55 } 56 catch (NumberFormatException e) 57 { 58 e.printStackTrace(); 59 _maxItemnames = 9; 60 } 61 if (_maxItemnames < 1) 62 { 63 _maxItemnames = 9; 64 } 65 66 _fileMenu = _owner.getFileMenu(); 67 } 68 69 ////////////////////////////////////////////////////////// 70 // Operations 71 /*** 72 * Initialize the file item and path lists from a configuration 73 * file and build up the additional entries in the File menu. 74 */ 75 public void initFileMenuHistory() 76 { 77 if (new File(_historyFile).exists()) 78 { 79 try 80 { 81 FileInputStream fis = new FileInputStream(_historyFile); 82 ObjectInputStream ois = new ObjectInputStream(fis); 83 int itemnameCount = ois.readInt(); 84 // if the user has reduced filehistory.size in the past: cut last items 85 if (itemnameCount > _maxItemnames) 86 { 87 itemnameCount = _maxItemnames; 88 } 89 if (itemnameCount > 0) 90 { 91 _fileMenu.addSeparator(); 92 } 93 for (int i = 0; i < itemnameCount; i++) 94 { 95 _itemnameHistory.add((String)ois.readObject()); 96 _pathnameHistory.add((String)ois.readObject()); 97 98 MenuItemWithFixedTooltip item = 99 new MenuItemWithFixedTooltip(i+1, (String) _itemnameHistory.get(i)); 100 item.setToolTipText((String) _pathnameHistory.get(i)); 101 item.addActionListener(new ItemListener(i)); 102 _fileMenu.add(item); 103 } 104 105 ois.close(); 106 fis.close(); 107 } 108 catch (Exception e) 109 { 110 System.err.println("Trouble reading file history entries: " + e); 111 e.printStackTrace(); 112 } 113 } 114 } 115 116 /*** 117 * Save the lists of file items and paths. 118 */ 119 public void saveHistoryEntries() 120 { 121 try 122 { 123 FileOutputStream fos = new FileOutputStream(_historyFile); 124 ObjectOutputStream oos = new ObjectOutputStream(fos); 125 int itemnameCount = _itemnameHistory.size(); 126 oos.writeInt(itemnameCount); 127 for (int i = 0; i < itemnameCount; i++) 128 { 129 oos.writeObject((String) _itemnameHistory.get(i)); 130 oos.writeObject((String) _pathnameHistory.get(i)); 131 } 132 oos.flush(); 133 oos.close(); 134 fos.close(); 135 } 136 catch (Exception e) 137 { 138 System.err.println("Trouble saving file history entries: " + e); 139 e.printStackTrace(); 140 } 141 } 142 143 /*** 144 * Insert the last loaded pathname into the File menu if it is not 145 * present yet. Only max pathnames are shown (the max number can be 146 * set in Jmon.ini; default is 9). Every item starts with 147 * "<i>: ", where <i> is in the range [1..max]. 148 * The loaded itemname will become item number 1 in the list. 149 */ 150 public final void insertPathname(String pathname) 151 { 152 for (int k = 0; k < _pathnameHistory.size(); k++) 153 { 154 if (((String) _pathnameHistory.get(k)).equals(pathname)) 155 { 156 int index = _fileMenu.getItemCount() - _itemnameHistory.size() + k; 157 _fileMenu.remove(index); 158 _pathnameHistory.remove(k); 159 _itemnameHistory.remove(k); 160 if (_itemnameHistory.isEmpty()) 161 { 162 //JSeparator is the last menu item at (index - 1) 163 _fileMenu.remove(index - 1); 164 } 165 insertPathname(pathname); 166 return; 167 } 168 } 169 170 if (_itemnameHistory.isEmpty()) 171 { 172 _fileMenu.addSeparator(); 173 } 174 else 175 { 176 // remove all itemname entries to prepare for re-arrangement 177 for (int i = _fileMenu.getItemCount() - 1, j = 0; j < _itemnameHistory.size(); i--, j++) 178 { 179 _fileMenu.remove(i); 180 } 181 } 182 183 if (_itemnameHistory.size() == _maxItemnames) 184 { 185 // fileList is full: remove last entry to get space for the first item 186 _itemnameHistory.remove(_maxItemnames - 1); 187 _pathnameHistory.remove(_maxItemnames - 1); 188 } 189 190 _itemnameHistory.add(0, getItemname(pathname)); 191 _pathnameHistory.add(0, pathname); 192 193 for (int i = 0; i < _itemnameHistory.size(); i++) 194 { 195 MenuItemWithFixedTooltip item = 196 new MenuItemWithFixedTooltip(i+1, (String) _itemnameHistory.get(i)); 197 item.setToolTipText((String) _pathnameHistory.get(i)); 198 item.addActionListener(new ItemListener(i)); 199 _fileMenu.add(item); 200 } 201 } 202 203 /*** 204 * Process the file history list that is appended to the file menu: 205 * display a dialog to delete itemname items. 206 */ 207 public void processList() 208 { 209 final JList itemList = createItemList(); 210 final JDialog dialog = new JDialog(_owner.getFrame(), "File History", true); 211 dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); 212 Container c = dialog.getContentPane(); 213 JScrollPane scroller = new JScrollPane(itemList); 214 scroller.setBorder( 215 BorderFactory.createCompoundBorder( 216 BorderFactory.createEmptyBorder(10, 10, 10, 10), 217 BorderFactory.createLoweredBevelBorder() 218 ) 219 ); 220 c.add(scroller, BorderLayout.CENTER); 221 222 JButton deleteB = new JButton("Delete"); 223 deleteB.addActionListener( 224 new ActionListener() 225 { 226 public void actionPerformed(ActionEvent e) 227 { 228 int[] indicesToDelete = itemList.getSelectedIndices(); 229 if (indicesToDelete.length > 0) 230 { 231 int oldFileHistorySize = _itemnameHistory.size(); 232 ArrayList itemnames = new ArrayList(oldFileHistorySize - indicesToDelete.length); 233 ArrayList pathnames = new ArrayList(oldFileHistorySize - indicesToDelete.length); 234 for (int i=0; i<oldFileHistorySize; i++) 235 { 236 boolean copyItem = true; 237 for (int j = 0; j < indicesToDelete.length; j++) 238 { 239 if (i == indicesToDelete[j]) 240 { 241 copyItem = false; 242 break; 243 } 244 } 245 if (copyItem) 246 { 247 itemnames.add(_itemnameHistory.get(i)); 248 pathnames.add(_pathnameHistory.get(i)); 249 } 250 } 251 _itemnameHistory = itemnames; 252 _pathnameHistory = pathnames; 253 itemList.revalidate(); 254 itemList.repaint(); 255 // re-arrange file menu history 256 for (int i = _fileMenu.getItemCount() - 1, j = 0; j < oldFileHistorySize; i--, j++) 257 { 258 _fileMenu.remove(i); 259 } 260 int lastIndex = _fileMenu.getItemCount() - 1; 261 for (int i = 0; i < _itemnameHistory.size(); i++) 262 { 263 MenuItemWithFixedTooltip item = 264 new MenuItemWithFixedTooltip(i+1, (String) _itemnameHistory.get(i)); 265 item.setToolTipText((String) _pathnameHistory.get(i)); 266 item.addActionListener(new ItemListener(i)); 267 _fileMenu.add(item); 268 } 269 if (_itemnameHistory.isEmpty()) 270 { 271 _fileMenu.remove(lastIndex); // no items were added: remove JSeparator too 272 } 273 } 274 } 275 } 276 ); 277 278 JButton closeB = new JButton("Close"); 279 closeB.setMaximumSize(deleteB.getPreferredSize()); 280 closeB.addActionListener( 281 new ActionListener() 282 { 283 public void actionPerformed(ActionEvent e) 284 { 285 dialog.hide(); 286 dialog.dispose(); 287 } 288 } 289 ); 290 291 JPanel buttonBox = new JPanel(); 292 buttonBox.setLayout(new BoxLayout(buttonBox, BoxLayout.Y_AXIS)); 293 buttonBox.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 10)); 294 buttonBox.add(Box.createVerticalStrut(10)); 295 buttonBox.add(deleteB); 296 buttonBox.add(Box.createVerticalStrut(10)); 297 buttonBox.add(closeB); 298 buttonBox.add(Box.createVerticalGlue()); 299 c.add(buttonBox, BorderLayout.EAST); 300 dialog.pack(); 301 dialog.setSize(350, 200); 302 // center dialog in parent frame 303 Dimension parentSize = _owner.getFrame().getSize(); 304 Dimension mySize = dialog.getSize(); 305 dialog.setLocation(parentSize.width/2 - mySize.width/2, 306 parentSize.height/2 - mySize.height/2); 307 dialog.setVisible(true); 308 } 309 310 /*** 311 * Return the itemname (abbreviated itemname if necessary) 312 * to be shown in the file menu open item list. 313 * A maximum of MAX_ITEM_LEN characters is used for the 314 * itemname because we do not want to make the JMenuItem 315 * entry too wide. 316 */ 317 protected String getItemname(String pathname) 318 { 319 final char FILE_SEPARATOR = FILE_SEPARATOR_STR.charAt(0); 320 final int pathnameLen = pathname.length(); 321 322 // if the pathame is short enough: return whole pathname 323 if (pathnameLen <= MAX_ITEM_LEN) 324 { 325 return pathname; 326 } 327 328 // if we have only one directory: return whole pathname 329 // we will not cut to MAX_ITEM_LEN here 330 if (pathname.indexOf(FILE_SEPARATOR_STR) == pathname.lastIndexOf(FILE_SEPARATOR_STR)) 331 { 332 return pathname; 333 } 334 else 335 { 336 // abbreviate pathanme: Windows OS like solution 337 final int ABBREVIATED_PREFIX_LEN = 6; // e.g.: C:\..\ 338 final int MAX_PATHNAME_LEN = MAX_ITEM_LEN - ABBREVIATED_PREFIX_LEN; 339 int firstFileSeparatorIndex = 0; 340 for (int i = pathnameLen - 1; i >= (pathnameLen - MAX_PATHNAME_LEN); i--) 341 { 342 if (pathname.charAt(i) == FILE_SEPARATOR) 343 { 344 firstFileSeparatorIndex = i; 345 } 346 } 347 if (firstFileSeparatorIndex > 0) 348 { 349 return pathname.substring(0, 3) 350 + ".." 351 + pathname.substring(firstFileSeparatorIndex, pathnameLen); 352 } 353 else 354 { 355 return pathname.substring(0, 3) 356 + ".." 357 + FILE_SEPARATOR_STR 358 + ".." 359 + pathname.substring(pathnameLen-MAX_PATHNAME_LEN, pathnameLen); 360 } 361 } 362 } 363 364 /*** 365 * Create a JList instance with _itemnameHistory as its model. 366 */ 367 private final JList createItemList() 368 { 369 ListModel model = new ListModel(); 370 JList list = new JList(model); 371 list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); 372 return list; 373 } 374 375 376 ////////////////////////////////////////////////////////// 377 // Inner classes 378 379 /*** 380 * Create a tooltip location directly over the menu item, 381 * ie, left allign the tooltip text in "overlay" technique. 382 */ 383 private final class MenuItemWithFixedTooltip extends JMenuItem 384 { 385 public MenuItemWithFixedTooltip(int position, String text) 386 { 387 super(position + ": " + text); 388 } 389 390 public Point getToolTipLocation(MouseEvent e) 391 { 392 Graphics g = getGraphics(); 393 FontMetrics metrics = g.getFontMetrics(g.getFont()); 394 String prefix = FileHistory.this._itemnameHistory.size() <= 9 ? "8: " : "88: "; 395 int prefixWidth = metrics.stringWidth(prefix); 396 int x = JButton.TRAILING + JButton.LEADING - 1 + prefixWidth; 397 return new Point(x, 0); 398 } 399 } /* end innser class MenuItemWithFixedTooltip */ 400 401 /*** 402 * Listen to menu item selections. 403 */ 404 private final class ItemListener implements ActionListener 405 { 406 private int _itemNbr; 407 408 ItemListener(int itemNbr) 409 { 410 _itemNbr = itemNbr; 411 } 412 413 public void actionPerformed(ActionEvent e) 414 { 415 _owner.loadFile((String) _pathnameHistory.get(_itemNbr)); 416 JMenuItem item = (JMenuItem) e.getSource(); 417 FileHistory.this.insertPathname(item.getToolTipText()); 418 } 419 } /* end inner class ItemListener */ 420 421 /*** 422 * The list model for our File History dialog itemList. 423 */ 424 private final class ListModel extends AbstractListModel 425 { 426 public Object getElementAt(int i) 427 { 428 return FileHistory.this._itemnameHistory.get(i); 429 } 430 431 public int getSize() 432 { 433 return FileHistory.this._itemnameHistory.size(); 434 } 435 } /* end inner class ListModel */ 436 437 } /* end class FileHistory */

This page was automatically generated by Maven