001: // Copyright (c) 2000, 2005 BlueJ Group, Deakin University
002: //
003: // This software is made available under the terms of the "MIT License"
004: // A copy of this license is included with this source distribution
005: // in "license.txt" and is also available at:
006: // http://www.opensource.org/licenses/mit-license.html
007: // Any queries should be directed to Michael Kolling mik@bluej.org
008:
009: package bluej.editor.moe;
010:
011: import javax.swing.FocusManager;
012: import java.awt.*;
013: import java.awt.event.*;
014: import javax.swing.*; // all the GUI components
015: //import javax.swing.KeyStroke;
016: import javax.swing.event.ListSelectionListener;
017: import javax.swing.event.ListSelectionEvent;
018:
019: import java.util.Properties;
020:
021: import bluej.Config;
022: import bluej.utility.Debug;
023: import bluej.utility.DialogManager;
024: import bluej.utility.EscapeDialog;
025: import bluej.utility.FixedMultiLineLabel;
026:
027: /**
028: ** Dialog to display user functions. The dialog displays function names,
029: ** help text and key bindings.
030: **
031: ** @author Michael Kolling
032: **
033: **/
034:
035: public final class FunctionDialog extends EscapeDialog
036:
037: implements ActionListener, ListSelectionListener, ItemListener {
038: // -------- CONSTANTS --------
039:
040: static final String title = Config
041: .getString("editor.functions.title");
042: static final String close = Config.getString("close");
043: static final String defaultsLabel = Config
044: .getString("editor.functions.defaults");
045: static final String categoriesLabel = Config
046: .getString("editor.functions.categories");
047: static final String keyLabel = Config
048: .getString("editor.functions.keys");
049: static final String addKeyLabel = Config
050: .getString("editor.functions.addkey");
051: static final String delKeyLabel = Config
052: .getString("editor.functions.delkey");
053:
054: // -------- INSTANCE VARIABLES --------
055:
056: private FocusManager focusMgr;
057: private JButton defaultsButton;
058: private JButton closeButton;
059: private JButton addKeyButton;
060: private JButton delKeyButton;
061: private JComboBox categoryMenu;
062: private JList functionList;
063: private JList keyList;
064: private FixedMultiLineLabel helpLabel;
065:
066: private MoeActions actions; // The Moe action manager
067: private Action currentAction; // the action currently selected
068: private KeyStroke[] currentKeys; // key strokes currently displayed
069:
070: private Action[] functions; // all user functions
071: private int[] categoryIndex; // an array of indexes into "functions"
072: private int firstDisplayedFunc; // index of first function in list
073: private Properties help;
074:
075: // ------------- METHODS --------------
076:
077: public FunctionDialog(JFrame parent, Action[] actiontable,
078: String[] categories, int[] categoryIndex) {
079: super (parent, title, true);
080: focusMgr = FocusManager.getCurrentManager();
081: actions = MoeActions.getActions(null);
082: currentAction = null;
083: currentKeys = null;
084: functions = actiontable;
085: this .categoryIndex = categoryIndex;
086: makeDialog(categories);
087: openHelpFile();
088: }
089:
090: /**
091: * Handle click on Close button.
092: */
093: private void handleClose() {
094: removeKeyListener();
095: if (!actions.save())
096: DialogManager.showError(this , "cannot-save-keys");
097: setVisible(false);
098: }
099:
100: /**
101: * Handle click on Defaults button.
102: */
103: private void handleDefaults() {
104: int answer = DialogManager.askQuestion(this , "default-keys");
105: if (answer == 0) {
106: actions.setDefaultKeyBindings();
107: handleFuncListSelect();
108: }
109: }
110:
111: /**
112: * Handle click in functions list.
113: */
114: private void handleFuncListSelect() {
115: int index = functionList.getSelectedIndex();
116: if (index == -1)
117: return; // deselection event - ignore
118:
119: // find selected action
120:
121: currentAction = functions[firstDisplayedFunc + index];
122:
123: // display keys and help text
124:
125: updateKeyList(currentAction);
126: String helpText = getHelpText((String) currentAction
127: .getValue(Action.NAME));
128: helpLabel.setText(helpText);
129: }
130:
131: /**
132: * Handle click in key bindings list.
133: */
134: private void handleKeyListSelect() {
135: delKeyButton.setEnabled(true);
136: }
137:
138: /**
139: * Handle click on Add Key button.
140: */
141: private void handleAddKey() {
142: helpLabel.setText(getHelpText("press-key"));
143: addKeyListener();
144: }
145:
146: /**
147: * Handle click on Delete Key button.
148: */
149: private void handleDelKey() {
150: if (currentKeys == null)
151: return; // something went wrong here...
152:
153: int index = keyList.getSelectedIndex();
154: if (index == -1)
155: return; // deselection event - ignore
156:
157: actions.removeKeyStrokeBinding(currentKeys[index]);
158: updateKeyList(currentAction);
159: }
160:
161: /**
162: * Display key bindings in the key list
163: */
164: private void updateKeyList(Action action) {
165: currentKeys = actions.getKeyStrokesForAction(action);
166: if (currentKeys == null)
167: clearKeyList();
168: else {
169: String[] keyStrings = getKeyStrings(currentKeys);
170: keyList.setListData(keyStrings);
171: delKeyButton.setEnabled(false);
172: }
173: addKeyButton.setEnabled(true);
174: }
175:
176: /**
177: * Translate KeyStrokes into String representation.
178: */
179: private String[] getKeyStrings(KeyStroke[] keys) {
180: String[] keyStrings = new String[keys.length];
181: for (int i = 0; i < keys.length; i++) {
182: int modifiers = keys[i].getModifiers();
183: keyStrings[i] = KeyEvent.getKeyModifiersText(modifiers);
184: if (keyStrings[i].length() > 0)
185: keyStrings[i] += "+";
186: keyStrings[i] += KeyEvent.getKeyText(keys[i].getKeyCode());
187: }
188: return keyStrings;
189: }
190:
191: private void clearKeyList() {
192: keyList.setListData(new String[0]);
193: }
194:
195: private void clearHelpText() {
196: helpLabel.setText(null);
197: }
198:
199: private void openHelpFile() {
200: help = Config.getMoeHelp();
201: }
202:
203: private String getHelpText(String function) {
204: if (help == null)
205: return null;
206: return help.getProperty(function);
207: }
208:
209: private void addKeyListener() {
210: FocusManager.setCurrentManager(new KeyCatcher());
211: }
212:
213: private void removeKeyListener() {
214: FocusManager.setCurrentManager(focusMgr);
215: }
216:
217: // ======== EVENT HANDLING INTERFACES =========
218:
219: // ----- ActionListener interface -----
220:
221: /**
222: * A button was pressed. Find out which one and do the appropriate
223: * thing.
224: */
225: public void actionPerformed(ActionEvent event) {
226: Object src = event.getSource();
227:
228: if (src == closeButton)
229: handleClose();
230: else if (src == defaultsButton)
231: handleDefaults();
232: else if (src == addKeyButton)
233: handleAddKey();
234: else if (src == delKeyButton)
235: handleDelKey();
236: }
237:
238: // ----- ItemListener interface -----
239:
240: /**
241: * The selected item in the category menu has changed.
242: */
243: public void itemStateChanged(ItemEvent evt) {
244: int selected = categoryMenu.getSelectedIndex();
245:
246: firstDisplayedFunc = categoryIndex[selected];
247: int lastFunc = categoryIndex[selected + 1];
248:
249: String[] names = new String[lastFunc - firstDisplayedFunc];
250:
251: for (int i = firstDisplayedFunc; i < lastFunc; i++) {
252: names[i - firstDisplayedFunc] = (String) functions[i]
253: .getValue(Action.NAME);
254: }
255: functionList.setListData(names);
256: clearKeyList();
257: clearHelpText();
258: addKeyButton.setEnabled(false);
259: delKeyButton.setEnabled(false);
260: currentAction = null;
261: currentKeys = null;
262: }
263:
264: // ----- ListSelectionListener interface -----
265:
266: /**
267: * The selected item in a list has changed.
268: */
269: public void valueChanged(ListSelectionEvent event) {
270: if (event.getValueIsAdjusting()) // ignore mouse down, dragging, etc.
271: return;
272:
273: Object src = event.getSource();
274:
275: if (src == functionList)
276: handleFuncListSelect();
277: else if (src == keyList)
278: handleKeyListSelect();
279: }
280:
281: // ----- end of ListSelectionListener interface -----
282:
283: private void makeDialog(String[] categories) {
284: JPanel mainPanel = (JPanel) getContentPane(); // has BorderLayout
285: mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10,
286: 10));
287:
288: // create help text area at bottom
289:
290: JPanel helpPanel = new JPanel(new GridLayout());
291: helpPanel.setBorder(BorderFactory.createCompoundBorder(
292: BorderFactory.createEmptyBorder(10, 0, 0, 0),
293: BorderFactory.createLineBorder(Color.black)));
294: helpLabel = new FixedMultiLineLabel(4);
295: helpLabel.setBackground(MoeEditor.infoColor);
296: helpPanel.add(helpLabel);
297: mainPanel.add(helpPanel, BorderLayout.SOUTH);
298:
299: // create control area on right (key bindings and buttons)
300:
301: JPanel controlPanel = new JPanel(new BorderLayout());
302:
303: // create area for main buttons (close, defaults)
304:
305: JPanel buttonPanel = new JPanel();
306: buttonPanel.setLayout(new GridLayout(0, 1, 5, 5));
307:
308: closeButton = new JButton(close);
309: closeButton.addActionListener(this );
310: buttonPanel.add(closeButton);
311:
312: defaultsButton = new JButton(defaultsLabel);
313: defaultsButton.addActionListener(this );
314: buttonPanel.add(defaultsButton);
315:
316: JPanel buttonFramePanel = new JPanel();
317: buttonFramePanel.setLayout(new BorderLayout(0, 0));
318: //buttonFramePanel.setBorder(BorderFactory.createEmptyBorder(0,10,0,0));
319: buttonFramePanel.add(buttonPanel, BorderLayout.NORTH);
320:
321: controlPanel.add(buttonFramePanel, BorderLayout.EAST);
322:
323: // create area for key bindings
324:
325: JPanel keyPanel = new JPanel();
326: keyPanel.setLayout(new BorderLayout());
327: keyPanel.setBorder(BorderFactory.createTitledBorder(
328: BorderFactory.createEtchedBorder(), keyLabel));
329:
330: keyList = new JList();
331: keyList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
332: keyList.setPrototypeCellValue("shift-ctrl-delete");
333: keyList.addListSelectionListener(this );
334: keyList.setVisibleRowCount(4);
335: JScrollPane scrollPane;
336: scrollPane = new JScrollPane(keyList);
337: keyPanel.add(scrollPane, BorderLayout.CENTER);
338:
339: JPanel keyButtonPanel = new JPanel();
340: addKeyButton = new JButton(addKeyLabel);
341: addKeyButton.addActionListener(this );
342: addKeyButton.setMargin(new Insets(2, 2, 2, 2));
343: keyButtonPanel.add(addKeyButton);
344:
345: delKeyButton = new JButton(delKeyLabel);
346: delKeyButton.addActionListener(this );
347: delKeyButton.setMargin(new Insets(2, 2, 2, 2));
348: keyButtonPanel.add(delKeyButton);
349:
350: keyPanel.add(keyButtonPanel, BorderLayout.SOUTH);
351:
352: controlPanel.add(keyPanel, BorderLayout.SOUTH);
353:
354: mainPanel.add(controlPanel, BorderLayout.EAST);
355:
356: // create function list area
357:
358: JPanel funcPanel = new JPanel(new BorderLayout());
359: funcPanel.setBorder(BorderFactory
360: .createEmptyBorder(0, 0, 0, 10));
361:
362: functionList = new JList();
363: functionList
364: .setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
365: functionList.addListSelectionListener(this );
366: functionList.setVisibleRowCount(12);
367: scrollPane = new JScrollPane(functionList);
368:
369: funcPanel.add(scrollPane, BorderLayout.CENTER);
370:
371: JPanel categoryPanel = new JPanel();
372:
373: JLabel label = new JLabel(categoriesLabel);
374: categoryPanel.add(label);
375: categoryMenu = new JComboBox();
376: categoryMenu.addItemListener(this );
377: for (int i = 0; i < categories.length; i++)
378: categoryMenu.addItem(categories[i]);
379: categoryPanel.add(categoryMenu);
380:
381: funcPanel.add(categoryPanel, BorderLayout.NORTH);
382:
383: mainPanel.add(funcPanel, BorderLayout.CENTER);
384: getRootPane().setDefaultButton(closeButton);
385:
386: addWindowListener(new WindowAdapter() {
387: public void windowClosing(WindowEvent E) {
388: setVisible(false);
389: }
390: });
391:
392: pack();
393: DialogManager.centreDialog(this );
394: }
395:
396: class KeyCatcher extends FocusManager {
397:
398: public void processKeyEvent(Component focusedComponent,
399: KeyEvent e) {
400: if (e.getID() != KeyEvent.KEY_PRESSED)
401: return;
402:
403: int keyCode = e.getKeyCode();
404:
405: if (keyCode == KeyEvent.VK_CAPS_LOCK
406: || // the keys we want to ignore...
407: keyCode == KeyEvent.VK_SHIFT
408: || keyCode == KeyEvent.VK_CONTROL
409: || keyCode == KeyEvent.VK_META
410: || keyCode == KeyEvent.VK_ALT
411: || keyCode == KeyEvent.VK_ALT_GRAPH
412: || keyCode == KeyEvent.VK_COMPOSE
413: || keyCode == KeyEvent.VK_NUM_LOCK
414: || keyCode == KeyEvent.VK_SCROLL_LOCK
415: || keyCode == KeyEvent.VK_UNDEFINED)
416: return;
417:
418: if (currentAction == null)
419: Debug
420: .message("FunctionDialog: currentAction is null...");
421: else {
422: KeyStroke key = KeyStroke.getKeyStrokeForEvent(e);
423: if (isPrintable(key, e))
424: helpLabel.setText(getHelpText("cannot-redefine"));
425: else {
426: actions.addActionForKeyStroke(key, currentAction);
427: handleFuncListSelect();
428: }
429: }
430: e.consume();
431: removeKeyListener();
432: }
433:
434: private boolean isPrintable(KeyStroke key, KeyEvent e) {
435: // all control and alt keys are non-printable
436: int modifiers = key.getModifiers();
437: if (modifiers != 0 && modifiers != Event.SHIFT_MASK)
438: return false;
439:
440: // action keys are non-printable
441: if (e.isActionKey())
442: return false;
443:
444: // some action keys that the above function not recognises
445: int keyCode = e.getKeyCode();
446: if (keyCode == KeyEvent.VK_BACK_SPACE
447: || keyCode == KeyEvent.VK_DELETE
448: || keyCode == KeyEvent.VK_ENTER
449: || keyCode == KeyEvent.VK_TAB
450: || keyCode == KeyEvent.VK_ESCAPE)
451: return false;
452:
453: // otherwise it's printable
454: return true;
455: }
456:
457: public void focusNextComponent(Component c) {
458: }
459:
460: public void focusPreviousComponent(Component c) {
461: }
462: }
463:
464: } // end class FunctionDialog
|