/*
* ActionBar.java - For invoking actions directly
* :tabSize=4:indentSize=4:noTabs=false:
* :folding=explicit:collapseFolds=1:
*
* Copyright (C) 2003 Slava Pestov
*
* 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.
*/
package org.gjt.sp.jedit.gui;
//{{{ Imports
import org.gjt.sp.jedit.bsh.NameSpace;
import java.awt.event.*;
import java.awt.*;
import java.util.ArrayList;
import javax.swing.event.*;
import javax.swing.*;
import org.gjt.sp.jedit.*;
import org.gjt.sp.util.GenericGUIUtilities;
import org.gjt.sp.util.StandardUtilities;
//}}}
/** Action invocation bar.
*/
public class ActionBar extends JToolBar
{
//{{{ ActionBar constructor
public ActionBar(View view, boolean temp)
{
this.view = view;
this.temp = temp;
setLayout(new BoxLayout(this,BoxLayout.X_AXIS));
setFloatable(false);
add(Box.createHorizontalStrut(2));
JLabel label = new JLabel(jEdit.getProperty("view.action.prompt"));
add(label);
add(Box.createHorizontalStrut(12));
add(action = new ActionTextField());
action.setEnterAddsToHistory(false);
Dimension max = action.getPreferredSize();
max.width = Integer.MAX_VALUE;
action.setMaximumSize(max);
action.addActionListener(new ActionHandler());
action.getDocument().addDocumentListener(new DocumentHandler());
if(temp)
{
close = new RolloverButton(GUIUtilities.loadIcon("closebox.gif"));
close.addActionListener(new ActionHandler());
close.setToolTipText(jEdit.getProperty(
"view.action.close-tooltip"));
add(close);
}
// if 'temp' is true, hide search bar after user is done with it
this.temp = temp;
} //}}}
//{{{ getField() method
public HistoryTextField getField()
{
return action;
} //}}}
//{{{ goToActionBar() method
public void goToActionBar()
{
repeatCount = view.getInputHandler().getRepeatCount();
action.setText(null);
action.requestFocus();
} //}}}
//{{{ Private members
private static NameSpace namespace = new NameSpace(
BeanShell.getNameSpace(),"action bar namespace");
//{{{ Instance variables
private View view;
private boolean temp;
private int repeatCount;
private HistoryTextField action;
private CompletionPopup popup;
private RolloverButton close;
//}}}
//{{{ invoke() method
private void invoke()
{
String cmd;
if(popup != null)
cmd = popup.list.getSelectedValue().toString();
else
{
cmd = action.getText().trim();
int index = cmd.indexOf('=');
if(index != -1)
{
action.addCurrentToHistory();
String propName = cmd.substring(0,index).trim();
String propValue = cmd.substring(index + 1).trim();
StringBuilder code = new StringBuilder(128);
/* construct a BeanShell snippet instead of
* invoking directly so that user can record
* property changes in macros. */
if(propName.startsWith("buffer."))
{
if(propName.equals("buffer.mode"))
{
code.append("buffer.setMode(\"")
.append(StandardUtilities.charsToEscapes(propValue))
.append("\");");
}
else
{
code.append("buffer.setStringProperty(\"")
.append(StandardUtilities.charsToEscapes(propName.substring("buffer.".length())))
.append("\",\"")
.append(StandardUtilities.charsToEscapes(propValue))
.append("\");");
}
code.append("\nbuffer.propertiesChanged();");
}
else if(propName.startsWith("!buffer."))
{
code.append("jEdit.setProperty(\"")
.append(StandardUtilities.charsToEscapes(propName.substring(1)))
.append("\",\"")
.append(StandardUtilities.charsToEscapes(propValue))
.append("\");\njEdit.propertiesChanged();");
}
else
{
code.append("jEdit.setProperty(\"")
.append(StandardUtilities.charsToEscapes(propName))
.append("\",\"")
.append(StandardUtilities.charsToEscapes(propValue))
.append("\");\njEdit.propertiesChanged();");
}
Macros.Recorder recorder = view.getMacroRecorder();
if(recorder != null)
recorder.record(code.toString());
BeanShell.eval(view, namespace, code.toString());
cmd = null;
}
else if(cmd.length() != 0)
{
String[] completions = getCompletions(cmd);
if(completions.length != 0)
{
cmd = completions[0];
}
}
else
cmd = null;
}
if(popup != null)
{
popup.dispose();
popup = null;
}
final String finalCmd = cmd;
final EditAction act = (finalCmd == null ? null : jEdit.getAction(finalCmd));
if(temp)
view.removeToolBar(this);
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
view.getTextArea().requestFocus();
if(act == null)
{
if(finalCmd != null)
{
view.getStatus().setMessageAndClear(
jEdit.getProperty(
"view.action.no-completions"));
}
}
else
{
view.getInputHandler().setRepeatCount(repeatCount);
view.getInputHandler().invokeAction(act);
}
}
});
} //}}}
//{{{ getCompletions() method
private static String[] getCompletions(String str)
{
str = str.toLowerCase();
String[] actions = jEdit.getActionNames();
ArrayList<String> returnValue = new ArrayList<String>(actions.length);
for (String act : actions)
{
if (act.toLowerCase().contains(str))
returnValue.add(act);
}
return returnValue.toArray(new String[returnValue.size()]);
} //}}}
//{{{ complete() method
private void complete(boolean insertLongestPrefix)
{
String text = action.getText().trim();
String[] completions = getCompletions(text);
if(completions.length == 1)
{
if(insertLongestPrefix)
action.setText(completions[0]);
}
else if(completions.length != 0)
{
if(insertLongestPrefix)
{
String prefix = MiscUtilities.getLongestPrefix(
completions,true);
if(prefix.contains(text))
action.setText(prefix);
}
if(popup != null)
popup.setModel(completions);
else
popup = new CompletionPopup(completions);
return;
}
if(popup != null)
{
popup.dispose();
popup = null;
}
} //}}}
//}}}
//{{{ Inner classes
//{{{ ActionHandler class
private class ActionHandler implements ActionListener
{
public void actionPerformed(ActionEvent evt)
{
if(evt.getSource() == close)
view.removeToolBar(ActionBar.this);
else
invoke();
}
} //}}}
//{{{ DocumentHandler class
private class DocumentHandler implements DocumentListener
{
//{{{ insertUpdate() method
public void insertUpdate(DocumentEvent evt)
{
if(popup != null)
complete(false);
} //}}}
//{{{ removeUpdate() method
public void removeUpdate(DocumentEvent evt)
{
if(popup != null)
complete(false);
} //}}}
//{{{ changedUpdate() method
public void changedUpdate(DocumentEvent evt) {}
//}}}
} //}}}
//{{{ ActionTextField class
private class ActionTextField extends HistoryTextField
{
boolean repeat;
boolean nonDigit;
ActionTextField()
{
super("action");
setSelectAllOnFocus(true);
}
@Override
public boolean getFocusTraversalKeysEnabled()
{
return false;
}
@Override
public void processKeyEvent(KeyEvent evt)
{
evt = KeyEventWorkaround.processKeyEvent(evt);
if(evt == null)
return;
switch(evt.getID())
{
case KeyEvent.KEY_TYPED:
char ch = evt.getKeyChar();
if(!nonDigit && Character.isDigit(ch))
{
super.processKeyEvent(evt);
repeat = true;
repeatCount = Integer.parseInt(action.getText());
}
else
{
nonDigit = true;
if(repeat)
{
passToView(evt);
}
else
super.processKeyEvent(evt);
}
break;
case KeyEvent.KEY_PRESSED:
int keyCode = evt.getKeyCode();
if(evt.isActionKey()
|| evt.isControlDown()
|| evt.isAltDown()
|| evt.isMetaDown()
|| keyCode == KeyEvent.VK_BACK_SPACE
|| keyCode == KeyEvent.VK_DELETE
|| keyCode == KeyEvent.VK_ENTER
|| keyCode == KeyEvent.VK_TAB
|| keyCode == KeyEvent.VK_ESCAPE)
{
nonDigit = true;
if(repeat)
{
passToView(evt);
break;
}
else if(keyCode == KeyEvent.VK_TAB)
{
complete(true);
evt.consume();
}
else if(keyCode == KeyEvent.VK_ESCAPE)
{
evt.consume();
if(popup != null)
{
popup.dispose();
popup = null;
action.requestFocus();
}
else
{
if(temp)
view.removeToolBar(ActionBar.this);
view.getEditPane().focusOnTextArea();
}
break;
}
else if((keyCode == KeyEvent.VK_UP
|| keyCode == KeyEvent.VK_DOWN)
&& popup != null)
{
popup.list.processKeyEvent(evt);
break;
}
}
super.processKeyEvent(evt);
break;
}
}
private void passToView(final KeyEvent evt)
{
if(temp)
view.removeToolBar(ActionBar.this);
view.getTextArea().requestFocus();
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
view.getTextArea().requestFocus();
view.getInputHandler().setRepeatCount(repeatCount);
view.getInputHandler().processKeyEvent(evt,
View.ACTION_BAR, false);
}
});
}
@Override
public void addNotify()
{
super.addNotify();
repeat = nonDigit = false;
}
} //}}}
//{{{ CompletionPopup class
private class CompletionPopup extends JWindow
{
CompletionList<String> list;
//{{{ CompletionPopup constructor
CompletionPopup(String[] actions)
{
super(view);
setContentPane(new JPanel(new BorderLayout())
{
/**
* Makes the tab key work in Java 1.4.
*/
@Override
public boolean getFocusTraversalKeysEnabled()
{
return false;
}
});
list = new CompletionList<String>(actions);
list.setVisibleRowCount(8);
list.addMouseListener(new MouseHandler());
list.setSelectedIndex(0);
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
// stupid scrollbar policy is an attempt to work around
// bugs people have been seeing with IBM's JDK -- 7 Sep 2000
JScrollPane scroller = new JScrollPane(list,
ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
getContentPane().add(scroller, BorderLayout.CENTER);
GenericGUIUtilities.requestFocus(this,list);
pack();
Point p = new Point(0,-getHeight());
SwingUtilities.convertPointToScreen(p,action);
setLocation(p);
setVisible(true);
KeyHandler keyHandler = new KeyHandler();
addKeyListener(keyHandler);
list.addKeyListener(keyHandler);
} //}}}
//{{{ setModel() method
void setModel(String[] actions)
{
list.setListData(actions);
list.setSelectedIndex(0);
} //}}}
//{{{ MouseHandler class
private class MouseHandler extends MouseAdapter
{
@Override
public void mouseClicked(MouseEvent evt)
{
invoke();
}
} //}}}
//{{{ CompletionList class
class CompletionList<E> extends JList<E>
{
CompletionList(E[] data)
{
super(data);
}
// we need this public not protected
@Override
public void processKeyEvent(KeyEvent evt) // NOPMD
{
super.processKeyEvent(evt);
}
} //}}}
//{{{ KeyHandler class
private class KeyHandler extends KeyAdapter
{
@Override
public void keyTyped(KeyEvent evt)
{
action.processKeyEvent(evt);
}
@Override
public void keyPressed(KeyEvent evt)
{
int keyCode = evt.getKeyCode();
if(keyCode == KeyEvent.VK_ESCAPE)
action.processKeyEvent(evt);
else if(keyCode == KeyEvent.VK_ENTER)
invoke();
else if(keyCode == KeyEvent.VK_UP)
{
int selected = list.getSelectedIndex();
if(selected == 0)
{
list.setSelectedIndex(
list.getModel().getSize()
- 1);
evt.consume();
}
}
else if(keyCode == KeyEvent.VK_DOWN)
{
int selected = list.getSelectedIndex();
if(selected == list.getModel().getSize() - 1)
{
list.setSelectedIndex(0);
evt.consume();
}
}
}
} //}}}
} //}}}
//}}}
}