001: /*
002: * ShortcutEditor.java
003: *
004: * This file is part of SQL Workbench/J, http://www.sql-workbench.net
005: *
006: * Copyright 2002-2008, Thomas Kellerer
007: * No part of this code maybe reused without the permission of the author
008: *
009: * To contact the author please send an email to: support@sql-workbench.net
010: *
011: */
012: package workbench.gui.settings;
013:
014: import java.awt.BorderLayout;
015: import java.awt.Dimension;
016: import java.awt.EventQueue;
017: import java.awt.FlowLayout;
018: import java.awt.Frame;
019: import java.awt.event.ActionEvent;
020: import java.awt.event.ActionListener;
021: import java.awt.event.MouseEvent;
022: import java.awt.event.MouseListener;
023: import java.awt.event.WindowEvent;
024: import java.awt.event.WindowListener;
025: import java.sql.Types;
026: import javax.swing.ActionMap;
027: import javax.swing.Box;
028: import javax.swing.InputMap;
029: import javax.swing.JButton;
030: import javax.swing.JComponent;
031: import javax.swing.JDialog;
032: import javax.swing.JOptionPane;
033: import javax.swing.JPanel;
034: import javax.swing.JRootPane;
035: import javax.swing.JScrollPane;
036: import javax.swing.JTable;
037: import javax.swing.KeyStroke;
038: import javax.swing.ListSelectionModel;
039: import javax.swing.WindowConstants;
040: import javax.swing.event.ListSelectionEvent;
041: import javax.swing.event.ListSelectionListener;
042: import javax.swing.table.TableColumn;
043: import workbench.gui.WbSwingUtilities;
044: import workbench.gui.WbSwingUtilities;
045: import workbench.gui.actions.ActionRegistration;
046: import workbench.gui.actions.EscAction;
047: import workbench.gui.components.DataStoreTableModel;
048: import workbench.gui.components.DividerBorder;
049: import workbench.gui.components.TableColumnOptimizer;
050: import workbench.gui.components.WbButton;
051: import workbench.gui.components.WbTable;
052: import workbench.resource.ResourceMgr;
053: import workbench.resource.Settings;
054: import workbench.resource.ShortcutDefinition;
055: import workbench.resource.ShortcutManager;
056: import workbench.resource.StoreableKeyStroke;
057: import workbench.storage.DataStore;
058:
059: /**
060: * @author support@sql-workbench.net
061: *
062: */
063: public class ShortcutEditor extends JPanel implements ActionListener,
064: ListSelectionListener, WindowListener, MouseListener {
065: private static final String KEY_WINDOW_SIZE = "workbench.shortcuteditor";
066: private WbTable keysTable;
067: private DataStore definitions;
068: private DataStoreTableModel model;
069: private JDialog window;
070: private Frame parent;
071:
072: private JButton okButton;
073: private JButton cancelButton;
074: private JButton assignButton;
075: private JButton resetButton;
076: private JButton resetAllButton;
077: private JButton clearButton;
078:
079: private String escActionCommand;
080:
081: public ShortcutEditor(Frame parent) {
082: this .parent = parent;
083:
084: // make sure actions that are not created upon startup are
085: // registered with us!
086: ActionRegistration.registerActions();
087: }
088:
089: public void showWindow() {
090: WbSwingUtilities.showWaitCursor(parent);
091: window = new JDialog(parent, ResourceMgr
092: .getString("LblConfigureShortcutWindowTitle"), true);
093: window
094: .setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
095: window.addWindowListener(this );
096: JPanel contentPanel = new JPanel(new BorderLayout());
097:
098: this .keysTable = new WbTable();
099: this .keysTable.useMultilineTooltip(false);
100: this .keysTable.setShowPopupMenu(false);
101: this .keysTable
102: .setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
103: this .setLayout(new BorderLayout());
104: JScrollPane scroll = new JScrollPane(this .keysTable);
105: contentPanel.add(scroll, BorderLayout.CENTER);
106:
107: JRootPane root = window.getRootPane();
108: InputMap im = root
109: .getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
110: ActionMap am = root.getActionMap();
111: EscAction esc = new EscAction(this );
112: escActionCommand = esc.getActionName();
113: im.put(esc.getAccelerator(), esc.getActionName());
114: am.put(esc.getActionName(), esc);
115:
116: this .createModel();
117: this .keysTable.setRowSelectionAllowed(true);
118: this .keysTable.getSelectionModel().setSelectionMode(
119: ListSelectionModel.SINGLE_SELECTION);
120: this .keysTable.setAdjustToColumnLabel(true);
121: TableColumnOptimizer optimizer = new TableColumnOptimizer(
122: this .keysTable);
123: optimizer.optimizeAllColWidth(80, -1, true);
124: this .keysTable.addMouseListener(this );
125: this .cancelButton = new WbButton(ResourceMgr
126: .getString("LblCancel"));
127: this .cancelButton.addActionListener(this );
128:
129: im = keysTable.getInputMap(JComponent.WHEN_FOCUSED);
130: am = keysTable.getActionMap();
131: im.put(esc.getAccelerator(), esc.getActionName());
132: am.put(esc.getActionName(), esc);
133:
134: this .okButton = new WbButton(ResourceMgr.getString("LblOK"));
135: this .okButton.addActionListener(this );
136:
137: JPanel buttonPanel = new JPanel();
138: buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
139: buttonPanel.add(this .okButton);
140: buttonPanel.add(this .cancelButton);
141: this .add(buttonPanel, BorderLayout.SOUTH);
142:
143: Box b = Box.createHorizontalBox();
144: Box editBox = Box.createVerticalBox();
145:
146: Dimension min = new Dimension(90, 24);
147: Dimension max = new Dimension(160, 24);
148:
149: this .assignButton = new WbButton(ResourceMgr
150: .getString("LblAssignShortcut"));
151: this .assignButton.setToolTipText(ResourceMgr
152: .getDescription("LblAssignShortcut"));
153: this .assignButton.addActionListener(this );
154: this .assignButton.setEnabled(false);
155: this .assignButton.setPreferredSize(min);
156: this .assignButton.setMinimumSize(min);
157: this .assignButton.setMaximumSize(max);
158:
159: this .clearButton = new WbButton(ResourceMgr
160: .getString("LblClearShortcut"));
161: this .clearButton.setToolTipText(ResourceMgr
162: .getDescription("LblClearShortcut"));
163: this .clearButton.addActionListener(this );
164: this .clearButton.setEnabled(false);
165: this .clearButton.setPreferredSize(min);
166: this .clearButton.setMinimumSize(min);
167: this .clearButton.setMaximumSize(max);
168:
169: this .resetButton = new WbButton(ResourceMgr
170: .getString("LblResetShortcut"));
171: this .resetButton.setToolTipText(ResourceMgr
172: .getDescription("LblResetShortcut"));
173: this .resetButton.addActionListener(this );
174: this .resetButton.setEnabled(false);
175: this .resetButton.setPreferredSize(min);
176: this .resetButton.setMinimumSize(min);
177: this .resetButton.setMaximumSize(max);
178:
179: this .resetAllButton = new WbButton(ResourceMgr
180: .getString("LblResetAllShortcuts"));
181: this .resetAllButton.setToolTipText(ResourceMgr
182: .getDescription("LblResetAllShortcuts"));
183: this .resetAllButton.addActionListener(this );
184: this .resetAllButton.setPreferredSize(min);
185: this .resetAllButton.setMinimumSize(min);
186: this .resetAllButton.setMaximumSize(max);
187:
188: editBox.add(Box.createVerticalStrut(2));
189: editBox.add(this .assignButton);
190: editBox.add(Box.createVerticalStrut(2));
191: editBox.add(this .clearButton);
192: editBox.add(Box.createVerticalStrut(15));
193: editBox.add(this .resetButton);
194: editBox.add(Box.createVerticalStrut(2));
195: editBox.add(this .resetAllButton);
196: editBox.add(Box.createVerticalGlue());
197: b.add(Box.createHorizontalStrut(10));
198: b.add(editBox);
199: b.add(Box.createHorizontalStrut(5));
200: contentPanel.add(b, BorderLayout.EAST);
201:
202: JPanel p = new JPanel();
203: Dimension d = new Dimension(1, 20);
204: p.setMinimumSize(d);
205: p.setPreferredSize(d);
206: p.setBorder(new DividerBorder(DividerBorder.HORIZONTAL_MIDDLE));
207: contentPanel.add(p, BorderLayout.SOUTH);
208:
209: p = new JPanel();
210: d = new Dimension(5, 1);
211: p.setMinimumSize(d);
212: p.setPreferredSize(d);
213: contentPanel.add(p, BorderLayout.WEST);
214:
215: p = new JPanel();
216: d = new Dimension(1, 5);
217: p.setMinimumSize(d);
218: p.setPreferredSize(d);
219: contentPanel.add(p, BorderLayout.NORTH);
220:
221: this .add(contentPanel, BorderLayout.CENTER);
222:
223: window.getContentPane().add(this );
224: if (!Settings.getInstance().restoreWindowSize(this .window,
225: KEY_WINDOW_SIZE)) {
226: window.setSize(600, 400);
227: }
228: WbSwingUtilities.center(window, parent);
229: WbSwingUtilities.showDefaultCursor(parent);
230: window.setVisible(true);
231: }
232:
233: private void createModel() {
234: ShortcutManager mgr = Settings.getInstance()
235: .getShortcutManager();
236: ShortcutDefinition[] keys = mgr.getDefinitions();
237:
238: String[] cols = new String[] {
239: ResourceMgr.getString("LblKeyDefCommandCol"),
240: ResourceMgr.getString("LblKeyDefKeyCol"),
241: ResourceMgr.getString("LblKeyDefDefaultCol") };
242: int[] types = new int[] { Types.VARCHAR, Types.OTHER,
243: Types.OTHER };
244:
245: this .definitions = new DataStore(cols, types);
246:
247: for (int i = 0; i < keys.length; i++) {
248: int row = this .definitions.addRow();
249: String cls = keys[i].getActionClass();
250: String title = mgr.getActionNameForClass(cls);
251: String tooltip = mgr.getTooltip(cls);
252: ActionDisplay disp = new ActionDisplay(title, tooltip);
253: this .definitions.setValue(row, 0, disp);
254: this .definitions.setValue(row, 1, new ShortcutDisplay(
255: keys[i], ShortcutDisplay.TYPE_PRIMARY_KEY));
256: this .definitions.setValue(row, 2, new ShortcutDisplay(
257: keys[i], ShortcutDisplay.TYPE_DEFAULT_KEY));
258: }
259: this .definitions.sortByColumn(0, true);
260: this .model = new DataStoreTableModel(this .definitions);
261: this .model.setAllowEditing(false);
262: this .keysTable.setModel(model, true);
263: TableColumn col = this .keysTable.getColumnModel().getColumn(0);
264: col.setCellRenderer(new ActionDisplayRenderer());
265: this .keysTable.getSelectionModel().addListSelectionListener(
266: this );
267: }
268:
269: public void actionPerformed(ActionEvent e) {
270: Object source = e.getSource();
271:
272: if (source == this .cancelButton) {
273: this .closeWindow();
274: } else if (source == this .okButton) {
275: this .saveShortcuts();
276: this .closeWindow();
277: } else if (source == this .assignButton) {
278: this .assignKey();
279: } else if (source == this .resetButton) {
280: this .resetCurrentKey();
281: } else if (source == this .resetAllButton) {
282: this .resetAllKeys();
283: } else if (source == this .clearButton) {
284: this .clearKey();
285: } else if (e.getActionCommand().equals(escActionCommand)) {
286: this .closeWindow();
287: }
288: }
289:
290: private void saveSettings() {
291: Settings.getInstance().storeWindowSize(this .window,
292: KEY_WINDOW_SIZE);
293: }
294:
295: private void saveShortcuts() {
296: ShortcutManager mgr = Settings.getInstance()
297: .getShortcutManager();
298: int count = this .definitions.getRowCount();
299: for (int row = 0; row < count; row++) {
300: ShortcutDisplay d = (ShortcutDisplay) this .definitions
301: .getValue(row, 1);
302: ShortcutDefinition def = d.getShortcut();
303: if (d.isModified()) {
304: if (d.isCleared()) {
305: mgr.removeShortcut(def.getActionClass());
306: } else if (d.doReset()) {
307: mgr.resetToDefault(def.getActionClass());
308: } else {
309: mgr.assignKey(def.getActionClass(), d.getNewKey()
310: .getKeyStroke());
311: }
312: }
313: }
314: mgr.updateActions();
315: }
316:
317: private void closeWindow() {
318: this .saveSettings();
319: this .window.setVisible(false);
320: this .window.dispose();
321: }
322:
323: public void valueChanged(ListSelectionEvent e) {
324: boolean enabled = (e.getFirstIndex() >= 0);
325: this .resetButton.setEnabled(enabled);
326: this .assignButton.setEnabled(enabled);
327: this .clearButton.setEnabled(enabled);
328: }
329:
330: private void assignKey() {
331: int row = this .keysTable.getSelectedRow();
332: if (row < 0)
333: return;
334: final KeyboardMapper mapper = new KeyboardMapper();
335: EventQueue.invokeLater(new Runnable() {
336: public void run() {
337: mapper.grabFocus();
338: }
339: });
340:
341: String[] options = new String[] {
342: ResourceMgr.getString("LblOK").replaceAll("&", ""),
343: ResourceMgr.getString("LblCancel").replaceAll("&", "") };
344:
345: JOptionPane overwritePane = new JOptionPane(mapper,
346: JOptionPane.PLAIN_MESSAGE,
347: JOptionPane.OK_CANCEL_OPTION, null, options);
348: JDialog dialog = overwritePane.createDialog(this , ResourceMgr
349: .getString("LblEnterKeyWindowTitle"));
350:
351: dialog.setResizable(true);
352: dialog.setVisible(true);
353: Object result = overwritePane.getValue();
354: dialog.dispose();
355:
356: if (options[0].equals(result)) {
357: KeyStroke key = mapper.getKeyStroke();
358: ShortcutDisplay d = (ShortcutDisplay) this .definitions
359: .getValue(row, 1);
360: String cls = d.getShortcut().getActionClass();
361:
362: int oldrow = this .findKey(key);
363: if (oldrow > -1) {
364: String name = this .definitions.getValueAsString(oldrow,
365: 0);
366: String msg = ResourceMgr.getString(
367: "MsgShortcutAlreadyAssigned").replaceAll(
368: "%action%", name);
369: boolean choice = WbSwingUtilities.getYesNo(this , msg);
370: if (!choice)
371: return;
372: ShortcutDisplay old = (ShortcutDisplay) this .definitions
373: .getValue(oldrow, 1);
374: old.clearKey();
375: this .model.fireTableRowsUpdated(oldrow, oldrow);
376: }
377:
378: d.setNewKey(key);
379: this .model.fireTableRowsUpdated(row, row);
380: }
381: }
382:
383: private void clearKey() {
384: int row = this .keysTable.getSelectedRow();
385: if (row < 0)
386: return;
387: ShortcutDisplay old = (ShortcutDisplay) this .definitions
388: .getValue(row, 1);
389: old.clearKey();
390: this .model.fireTableRowsUpdated(row, row);
391: }
392:
393: private void resetCurrentKey() {
394: int row = this .keysTable.getSelectedRow();
395: if (row < 0)
396: return;
397: ShortcutDisplay d = (ShortcutDisplay) this .definitions
398: .getValue(row, 1);
399: d.resetToDefault();
400: this .model.fireTableRowsUpdated(row, row);
401: WbSwingUtilities.repaintNow(this );
402: }
403:
404: private void resetAllKeys() {
405: int selected = this .keysTable.getSelectedRow();
406: int count = this .keysTable.getRowCount();
407: for (int row = 0; row < count; row++) {
408: ShortcutDisplay d = (ShortcutDisplay) this .definitions
409: .getValue(row, 1);
410: d.resetToDefault();
411: }
412: this .model.fireTableDataChanged();
413: if (selected > -1) {
414: this .keysTable.getSelectionModel().setSelectionInterval(
415: selected, selected);
416: }
417: }
418:
419: private int findKey(KeyStroke key) {
420: int count = this .definitions.getRowCount();
421: for (int row = 0; row < count; row++) {
422: ShortcutDisplay d = (ShortcutDisplay) this .definitions
423: .getValue(row, 1);
424: if (!d.isCleared() && d.isMappedTo(key)) {
425: return row;
426: }
427: }
428: return -1;
429: }
430:
431: public void windowActivated(WindowEvent e) {
432: }
433:
434: public void windowClosed(WindowEvent e) {
435: }
436:
437: public void windowClosing(WindowEvent e) {
438: this .closeWindow();
439: }
440:
441: public void windowDeactivated(WindowEvent e) {
442: }
443:
444: public void windowDeiconified(WindowEvent e) {
445: }
446:
447: public void windowIconified(WindowEvent e) {
448: }
449:
450: public void windowOpened(WindowEvent e) {
451: }
452:
453: public void mouseClicked(MouseEvent e) {
454: if (e.getSource() == this .keysTable && e.getClickCount() == 2
455: && e.getButton() == MouseEvent.BUTTON1) {
456: this .assignKey();
457: }
458: }
459:
460: public void mouseEntered(MouseEvent e) {
461: }
462:
463: public void mouseExited(MouseEvent e) {
464: }
465:
466: public void mousePressed(MouseEvent e) {
467: }
468:
469: public void mouseReleased(MouseEvent e) {
470: }
471: }
472:
473: class ShortcutDisplay {
474: public static final int TYPE_DEFAULT_KEY = 1;
475: public static final int TYPE_PRIMARY_KEY = 2;
476: public static final int TYPE_ALTERNATE_KEY = 3;
477:
478: private boolean isModified = false;
479: private int displayType;
480: private ShortcutDefinition shortcut;
481: private boolean clearKey = false;
482: private boolean resetToDefault = false;
483:
484: private StoreableKeyStroke newKey = null;
485:
486: ShortcutDisplay(ShortcutDefinition def, int type) {
487: this .shortcut = def;
488: this .displayType = type;
489: }
490:
491: public ShortcutDefinition getShortcut() {
492: return this .shortcut;
493: }
494:
495: public boolean isModified() {
496: return this .isModified;
497: }
498:
499: public boolean isCleared() {
500: return this .clearKey;
501: }
502:
503: public void clearKey() {
504: this .newKey = null;
505: this .clearKey = true;
506: this .isModified = true;
507: this .resetToDefault = false;
508: }
509:
510: public void setNewKey(KeyStroke aKey) {
511: this .newKey = new StoreableKeyStroke(aKey);
512: this .isModified = true;
513: this .resetToDefault = false;
514: this .clearKey = false;
515: }
516:
517: public StoreableKeyStroke getNewKey() {
518: return this .newKey;
519: }
520:
521: public boolean isMappedTo(KeyStroke aKey) {
522: boolean mapped = false;
523: if (newKey != null) {
524: mapped = newKey.equals(aKey);
525: }
526: if (!mapped) {
527: mapped = this .shortcut.isMappedTo(aKey);
528: }
529: return mapped;
530: }
531:
532: public boolean doReset() {
533: return this .resetToDefault;
534: }
535:
536: public void resetToDefault() {
537: this .isModified = true;
538: this .newKey = null;
539: this .clearKey = false;
540: this .resetToDefault = true;
541: }
542:
543: public String toString() {
544: StoreableKeyStroke key = null;
545: switch (this .displayType) {
546: case TYPE_DEFAULT_KEY:
547: key = this .shortcut.getDefaultKey();
548: break;
549: case TYPE_PRIMARY_KEY:
550: if (this .clearKey) {
551: key = null;
552: } else if (this .resetToDefault) {
553: key = this .shortcut.getDefaultKey();
554: } else if (this .newKey == null) {
555: key = this .shortcut.getActiveKey();
556: } else {
557: key = this .newKey;
558: }
559: break;
560: case TYPE_ALTERNATE_KEY:
561: key = this .shortcut.getAlternateKey();
562: break;
563: }
564: if (key == null)
565: return "";
566: return key.toString();
567: }
568: }
|