/*
 * Display_shortcuts.bsh - a BeanShell macro script for the
 * jEdit text editor - displays all shortcut key assignments
 * Copyright (C) 2003 Russell Inman
 * email: RInman@csustan.edu
 *
 * Code based on Display_Shortcuts.bsh, 1.3
 * Copyright (C) 2001 John Gellene
 * email: jgellene@nyc.rr.com
 * http://community.jedit.org
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 *
 * Display_Shortcuts.bsh, v1.3.1 2003-11-13
 *
 *
 * requires jEdit3.1pre5
 *
 * Notes on use:
 *
 * This macro will display a sorted list of all keyboard shortcuts
 * in a dialog.
 *
 * Pressing a letter key will cause the table to scroll to the first row
 * with a label beginning with the letter (or the imeediately preceding row if
 * no item begins with that letter).  The table is read-only; the dialog is
 * dismissed by clicking "OK" or pressing Esc or Enter.
 *
 * General comment:
 *
 * This macro illustrates the limitations of BeanShell, which cannot create
 * true derived subclasses.  To get the same features, the alternative to using
 * Vectors to hold the data would be to write a highly customized sorting
 * routine.  Sorting by keyname would require a second custom sorting routine.
 * There is also a hack in the code creating the table.
 *
 * Checked for jEdit 4.2pre6 API
 */

import javax.swing.table.*;

// Localization
final static String jEditShortcutTableLabel = jEdit.getProperty("macro.rs.DisplayShortcuts.jEditShortcutTable.label", "jEdit Keyboard Shortcut Table");
final static String NameLabel = jEdit.getProperty("macro.rs.DisplayShortcuts.Name.label", "Name");
final static String Shortcut1Label = jEdit.getProperty("macro.rs.DisplayShortcuts.Shortcut1.label", "Shortcut - 1");
final static String Shortcut2Label = jEdit.getProperty("macro.rs.DisplayShortcuts.Shortcut2.label", "Shortcut - 2");
final static String CloseLabel = jEdit.getProperty("macro.rs.DisplayShortcuts.Close.label", "Close");
final static String WriteToBufferLabel = jEdit.getProperty("macro.rs.DisplayShortcuts.WriteToBuffer.label", "Write to buffer");
final static String ShortcutListLabel = jEdit.getProperty("macro.rs.DisplayShortcuts.ShortcutList.label", "Keyboard shortcut list");

/*
 * method for creating vectors of row data describing shortcuts.
 */

void makeShortcutsVector(Vector p_v)
{
	String[] actionNames = jEdit.getActionNames();
	for(int i = 0; i < actionNames.length; i++)
	{
		EditAction action = jEdit.getAction(actionNames[i]);
		String name = action.getName();
		String label = action.getLabel();
	if(label == null)
			label = name;
		else label = GUIUtilities.prettifyMenuLabel(label);
		String shortcut1 = jEdit.getProperty(name + ".shortcut");
		if(shortcut1 == null)
			shortcut1 = "";
		String shortcut2 = jEdit.getProperty(name + ".shortcut2");
		if(shortcut2 == null)
			shortcut2 = "";
		if(shortcut1.length() != 0 || shortcut2.length() != 0)
		{
			p_v.addElement(makeRow(label,shortcut1,shortcut2));
		}
	}
}

/*
 * helper method to make vector of row data for table
 */
Vector makeRow(String name, String shortcut1, String shortcut2)
{
	Vector row = new Vector(3);
	row.addElement(name);
	row.addElement(shortcut1);
	row.addElement(shortcut2);
	return row;
}

/*
 * methods for formatting and writing shortcut data to a text buffer
 */

void writeTableToNewBuffer(Vector v)
{
	jEdit.newFile(view);
	textArea.setSelectedText(jEditShortcutTableLabel + "\n\n");
	headings = makeRow(NameLabel, Shortcut1Label, Shortcut2Label);
	writeLine(headings);
	textArea.setSelectedText("\n");
	for(int i = 0; i < v.size(); ++i)
	{
		writeLine((Vector)v.elementAt(i));
	}
}

void writeLine(Vector row)
{
	StringBuffer sb = new StringBuffer(85);
	spaceString = "                                        ";
	char[] space = spaceString.toCharArray();
	displayName = row.elementAt(0);
	if(displayName.length() > 38)
		displayName = displayName.substring(0, 34) + "...";
	sb.append(displayName);
	sb.append(space, 0, 40 - (displayName.length()));
	shortcut1 = row.elementAt(1);
	if(shortcut1 != null)
	{
		sb.append(shortcut1);
		sb.append(space, 0, 20 - (shortcut1.length()));
	}
	else sb.append(space, 0, 20);
	shortcut2 = row.elementAt(2);
	if(shortcut2 != null)
	{
		sb.append(shortcut2);
		sb.append(space, 0, 20 - (shortcut2.length()));
	}
	sb.append('\n');
	textArea.setSelectedText(sb.toString());
}

/*
 *  main routine
 */
void showShortcuts(View view)
{
	this.view = view;

	Vector v = new Vector();
	makeShortcutsVector(v);
	Collections.sort(v, new StandardUtilities.StringCompare());

	table = new JTable(v, makeRow( NameLabel, Shortcut1Label, Shortcut2Label));
	table.setRowHeight(GUIUtilities.defaultRowHeight());
	table.getColumnModel().getColumn(0).setPreferredWidth(200);
	table.setRowSelectionAllowed(true);
	/* 	The next line prevents the table from being edited.
	 * 	The normal approach in Java would be to subclass the TableModel
	 * 	associated with the JTable and define TableModel.isCellEditable()
	 * 	to return false.  However, BeanShell does not allow conventional
	 *  class creation, and the desired behavior cannot be achieved using
	 *  its scripted object feature.
	 */
	table.setDefaultEditor(Object.class, null);
	if(table.getRowCount() != 0)
	{
		table.setRowSelectionInterval(0, 0);
		table.setColumnSelectionInterval(1, 1);
	}

	tablePane = new JScrollPane(table);
	tablePane.setPreferredSize(new Dimension(450, 300));

	close = new JButton(CloseLabel);
	close.addActionListener(this);
	write = new JButton(WriteToBufferLabel);
	write.addActionListener(this);
	void actionPerformed(e)
	{
		if(e.getSource().getText().equals(CloseLabel))
			dialog.hide();
		else writeTableToNewBuffer(v);
	}
	buttonPanel = new JPanel(new FlowLayout());
	buttonPanel.add(write);
	buttonPanel.add(close);

	title = ShortcutListLabel;
	dialog = new JDialog(view, title, false);
	dialog.getContentPane().add(tablePane, "Center");
	dialog.getContentPane().add(buttonPanel, "South");
	dialog.getRootPane().setDefaultButton(close);


	table.addKeyListener(this);
	void keyPressed(e)
	{
		if(e.getKeyCode() == KeyEvent.VK_ESCAPE ||
			e.getKeyCode() == KeyEvent.VK_ENTER)
		{
			dialog.hide();
		}
		else
		{
			char ch = e.getKeyChar();
			if(Character.isLetter(ch))
			{
				e.consume();
				row = findFirstItem(ch);
				/* The next few lines set the last visible row
				 * of the table so that you can look ahead of
				 * the selected row.
				 */
				visibleRows =
					table.getVisibleRect().height / table.getRowHeight();
				oldRow = table.getSelectedRow();
				table.setRowSelectionInterval(row,row);
				if (visibleRows > 5 && row - oldRow > visibleRows - 3)
				{
					row = Math.min( v.size() - 1, row + 3);
				}
				table.scrollRectToVisible(table.getCellRect(row,0,true));
			}
		}
	}
	/*
	 * Having these members of KeyListener implemented will speedup execution;
	 * BeanShell will otherwise throw and handle an exception.
	 * This idiom is required under BeanShell 1.2
	 */
	void keyReleased(e) {}
	void keyTyped(e) {}


	/*
	 * A simple linear search for the table entry that begins with the
	 * given letter.  It returns the first row with an entry beginning with
	 * the letter, or the immdediately preceding row if there is no match
	 * on the letter.  If PUT_MACROS_AT_END is set to true, they will not be
	 * searched.
	 *
	 */
	int data_midpoint = 0;
	int findFirstItem(char ch)
	{
		ch = Character.toUpperCase(ch);
		int row = 0;
		int fix_err = ch > 'L' ? data_midpoint : 0;
		for(int i = fix_err; i < v.size(); ++i)
		{
			String name = ((Vector)v.elementAt(i)).elementAt(0);
			char ch_test = Character.toUpperCase(name.charAt(0));
			if( ch_test > ch) break;
			else
			{
				row = i;
				if( ch_test == ch) break;
			}
		}
		return row;
	}
	/* This line caches the row that starts the second half of the
	 * alphabet to speed up searches.  'M' was chosen as the midpoint
	 * to speed up searches.
	 */
	data_midpoint = findFirstItem('M');
	dialog.pack();
	dialog.setLocationRelativeTo(view);
	dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
	dialog.show();
}

showShortcuts(view);

/*
	Macro index data (in DocBook format)

<listitem>
    <para><filename>Display_Shortcuts.bsh</filename></para>
    <abstract><para>
        Displays a sorted list of the keyboard shortcuts currently in effect.
    </para></abstract>
    <para>
        The macro provides a combined read-only view of command, macro
        and plugin shortcuts. Pressing a letter key will
        scroll the table to the first entry beginning with that letter.
        A further option is provided to write the shortcut assignments in a
        text buffer for printing as a reference. Notes in the source code
        listing point out some display options that are configured by
        modifying global variables.
    </para>
</listitem>

*/

/*
	Changes from v1.3
	- Since makeShortcutsVector() had no return statement, the return type was
	  changed to void.
	- Removed deprecated jEdit.getEditActions() call from makeShortcutsVector
	- The original makeShortcutsVector method referenced a variable v, 
	  presumably from showShortcuts(), which was outside of its scope.  
	  I don't know if some new scoping rule in BeanShell broke this or not.
	  At any rate, the method has been updated so that it takes a 
	  Vector as a parameter.
	- Added an explicit declaration for Vector v to showShortcuts in order to
	  pass it to makeShortcutsVector
*/

// end Display_Shortcuts.bsh

