001: /*
002: * The contents of this file are subject to the Mozilla Public License
003: * Version 1.1 (the "License"); you may not use this file except in
004: * compliance with the License. You may obtain a copy of the License at
005: * http://www.mozilla.org/MPL/
006: *
007: * Software distributed under the License is distributed on an "AS IS"
008: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
009: * License for the specific language governing rights and limitations
010: * under the License.
011: *
012: * The Original Code is iSQL-Viewer, A Mutli-Platform Database Tool.
013: *
014: * The Initial Developer of the Original Code is iSQL-Viewer, A Mutli-Platform Database Tool.
015: * Portions created by Mark A. Kobold are Copyright (C) 2000-2007. All Rights Reserved.
016: *
017: * Contributor(s):
018: * Mark A. Kobold [mkobold <at> isqlviewer <dot> com].
019: *
020: * If you didn't download this code from the following link, you should check
021: * if you aren't using an obsolete version: http://www.isqlviewer.com
022: */
023: package org.isqlviewer.swing;
024:
025: import java.awt.Component;
026: import java.awt.Dimension;
027: import java.awt.Point;
028: import java.awt.Rectangle;
029: import java.awt.Toolkit;
030: import java.awt.event.InputEvent;
031: import java.awt.event.KeyEvent;
032: import java.io.File;
033: import java.io.IOException;
034: import java.net.URL;
035: import java.text.MessageFormat;
036: import java.util.ResourceBundle;
037: import java.util.StringTokenizer;
038:
039: import javax.swing.Icon;
040: import javax.swing.ImageIcon;
041: import javax.swing.JFileChooser;
042: import javax.swing.JFrame;
043: import javax.swing.JOptionPane;
044: import javax.swing.JRootPane;
045: import javax.swing.KeyStroke;
046: import javax.swing.RootPaneContainer;
047: import javax.swing.filechooser.FileFilter;
048: import javax.swing.text.BadLocationException;
049: import javax.swing.text.Document;
050: import javax.swing.text.Element;
051: import javax.swing.text.JTextComponent;
052: import javax.swing.text.Utilities;
053:
054: import org.isqlviewer.ui.laf.NullIcon;
055:
056: /**
057: * Customized swing utilities for iSQL-Viewer.
058: * <p>
059: *
060: * @author Mark A. Kobold
061: * @version 1.0
062: */
063: public final class SwingUtilities {
064:
065: public static final int MENU_SHORTCUT_MASK = Toolkit
066: .getDefaultToolkit().getMenuShortcutKeyMask();
067: public static final String WINDOW_MODIFIED = "windowModified";
068: private static final Icon NULL_ICON = new NullIcon();
069: /*
070: * // see getKeyStroke (String)
071: */
072: private static ModifierKeyword[] modifierKeywords = {
073: new ModifierKeyword("shift", InputEvent.SHIFT_MASK),
074: new ModifierKeyword("control", InputEvent.CTRL_MASK),
075: new ModifierKeyword("ctrl", InputEvent.CTRL_MASK),
076: new ModifierKeyword("meta", MENU_SHORTCUT_MASK),
077: new ModifierKeyword("alt", InputEvent.ALT_MASK),
078: new ModifierKeyword("button1", InputEvent.BUTTON1_MASK),
079: new ModifierKeyword("button2", InputEvent.BUTTON2_MASK),
080: new ModifierKeyword("button3", InputEvent.BUTTON3_MASK) };
081:
082: private SwingUtilities() {
083:
084: }
085:
086: /**
087: *
088: */
089: public static void beep() {
090:
091: Toolkit toolkit = Toolkit.getDefaultToolkit();
092: toolkit.beep();
093: }
094:
095: public static boolean isMacOS() {
096:
097: String lcOSName = System.getProperty("os.name").toLowerCase();
098: return lcOSName.startsWith("mac os x");
099: }
100:
101: public static void setFrameModified(RootPaneContainer frame,
102: Boolean flag) {
103:
104: synchronized (frame) {
105: JRootPane rootPane = frame.getRootPane();
106: rootPane.putClientProperty(WINDOW_MODIFIED, flag);
107: }
108: }
109:
110: public static void setFrameModified(RootPaneContainer frame,
111: boolean flag) {
112:
113: setFrameModified(frame, Boolean.valueOf(flag));
114: }
115:
116: public static void replaceDocumentContent(Document document,
117: String content) {
118:
119: try {
120: document.remove(0, document.getLength());
121: document.insertString(0, content, null);
122: } catch (BadLocationException error) {
123:
124: }
125: }
126:
127: public static void centerFrameOnScreen(JFrame frame) {
128:
129: Toolkit toolkit = Toolkit.getDefaultToolkit();
130: Dimension screenSize = toolkit.getScreenSize();
131: Dimension frameSize = frame.getSize();
132: if (frameSize.height > screenSize.height) {
133: frameSize.height = screenSize.height;
134: }
135: if (frameSize.width > screenSize.width) {
136: frameSize.width = screenSize.width;
137: }
138: Point p = new Point((screenSize.width - frameSize.width) / 2,
139: (screenSize.height - frameSize.height) / 2);
140: frame.setLocation(p);
141: }
142:
143: public static Boolean isFrameModified(RootPaneContainer frame) {
144:
145: synchronized (frame) {
146: JRootPane rootPane = frame.getRootPane();
147: Object obj = rootPane.getClientProperty(WINDOW_MODIFIED);
148: if (obj == null || !(obj instanceof Boolean)) {
149: return Boolean.FALSE;
150: }
151: return (Boolean) obj;
152: }
153: }
154:
155: public static void setPreferredGeometry(JFrame frame) {
156:
157: Toolkit tk = Toolkit.getDefaultToolkit();
158: Dimension frameSize = frame.getPreferredSize();
159: Dimension screenSize = tk.getScreenSize();
160:
161: if (frameSize.height > screenSize.height) {
162: frameSize.height = screenSize.height;
163: }
164:
165: if (frameSize.width > screenSize.width) {
166: frameSize.width = screenSize.width;
167: }
168:
169: Point p = new Point((screenSize.width - frameSize.width) / 2,
170: (screenSize.height - frameSize.height) / 2);
171: Rectangle rect = new Rectangle(p, frameSize);
172: frame.setBounds(rect);
173: }
174:
175: public static Icon loadIconResource(String img) {
176:
177: return loadIconResource(SwingUtilities.class, img, 0);
178: }
179:
180: public static Icon loadIconResource(String img, int size) {
181:
182: return loadIconResource(SwingUtilities.class, img, size);
183: }
184:
185: public static Icon loadIconResource(Class owner, String img) {
186:
187: return loadIconResource(owner, img, 0);
188: }
189:
190: public static Icon loadIconResource(Class owner, String img,
191: int size) {
192:
193: if (size <= 0) {
194: try {
195: String icon = MessageFormat.format("icons/{0}.png",
196: new Object[] { img });
197: return new ImageIcon(getResourceURL(owner, icon));
198: } catch (Exception ignored) {
199: return NULL_ICON;
200: }
201: }
202: try {
203: String icon = MessageFormat.format("icons/x{1}/{0}.png",
204: new Object[] { img, new Integer(size) });
205: return new ImageIcon(getResourceURL(owner, icon));
206: } catch (Exception ignored) {
207: return NULL_ICON;
208: }
209: }
210:
211: /**
212: * Creates a KeyStroke from a ResourceBundle string.
213: * <p>
214: *
215: * @see #getKeyStroke(String)
216: * @param bundle to load the localized text from.
217: * @param str ResourceBundle key.
218: * @return KeyStroke for the resource string can be null if not a valid keystroke.
219: */
220: public static KeyStroke getLocalKeyStroke(ResourceBundle bundle,
221: String str) {
222:
223: try {
224: return getKeyStroke(bundle.getString(str));
225: } catch (Exception e) {
226: return null;
227: }
228: }
229:
230: /**
231: * Utility method for creating KeyStrokes.
232: * <p>
233: * Synonym for 'KeyStroke.getKeyStroke(vk, mask, false)'
234: *
235: * @param VK constant for the Key
236: * @param Mask standared input mask
237: * @return KeyStroke based on the given parameters.
238: */
239: public static KeyStroke createKeyStroke(int VK, int Mask) {
240:
241: return KeyStroke.getKeyStroke(VK, Mask, false);
242: }
243:
244: /**
245: * Method getCaretCol.
246: *
247: * @param idx
248: * @param comp
249: * @return int
250: * @throws Exception
251: */
252: public static int getCaretCol(int idx, JTextComponent comp)
253: throws BadLocationException {
254:
255: return Math.abs(Utilities.getRowStart(comp, idx) - idx);
256: }
257:
258: /**
259: * Method getCaretRow.
260: *
261: * @param idx
262: * @param comp
263: * @return int
264: * @throws Exception
265: */
266: public static int getCaretRow(int idx, JTextComponent comp)
267: throws BadLocationException {
268:
269: return getLineOfOffset(idx, comp.getDocument());
270: }
271:
272: /**
273: * Consistent way to chosing multiple files to open with JFileChooser.
274: * <p>
275: *
276: * @see JFileChooser#setFileSelectionMode(int)
277: * @see #getSystemFiles(Component, int)
278: * @param owner to show the component relative to.
279: * @param mode selection mode for the JFileChooser.
280: * @return File[] based on the user selection can be null.
281: */
282: public static File[] getSystemFiles(Component owner, int mode,
283: FileFilter[] filters) {
284:
285: JFileChooser jfc = new JFileChooser();
286: jfc.setFileSelectionMode(mode);
287: jfc.setFileHidingEnabled(true);
288: jfc.setMultiSelectionEnabled(true);
289: jfc.setAcceptAllFileFilterUsed(true);
290: if (filters != null) {
291: for (int i = 0; i < filters.length; i++) {
292: jfc.addChoosableFileFilter(filters[i]);
293: }
294:
295: if (filters.length >= 1) {
296: jfc.setFileFilter(filters[0]);
297: }
298: }
299:
300: int result = jfc.showOpenDialog(owner);
301: if (result == JFileChooser.APPROVE_OPTION) {
302: return jfc.getSelectedFiles();
303: }
304:
305: return new File[0];
306: }
307:
308: public static File[] getSystemFiles(Component owner, int mode) {
309:
310: return getSystemFiles(owner, mode, null);
311: }
312:
313: /**
314: * Consistent way to chosing multiple files to save with JFileChooser.
315: * <p>
316: *
317: * @see #saveSystemFile(Component)
318: * @param owner to show the component relative to.
319: * @return File[] based on the user selection can be null.
320: */
321: public static File[] saveSystemFiles(Component owner) {
322:
323: JFileChooser jfc = new JFileChooser();
324: jfc.setFileHidingEnabled(true);
325: jfc.setMultiSelectionEnabled(true);
326: int result = jfc.showSaveDialog(owner);
327:
328: if (result == JFileChooser.APPROVE_OPTION) {
329: return jfc.getSelectedFiles();
330: }
331:
332: return new File[0];
333: }
334:
335: /**
336: * Consistent way to chosing a file to open with JFileChooser.
337: * <p>
338: *
339: * @see JFileChooser#setFileSelectionMode(int)
340: * @see #getSystemFiles(Component, int)
341: * @param owner to show the component relative to.
342: * @param mode selection mode for the JFileChooser.
343: * @return File based on the user selection can be null.
344: */
345: public static File getSystemFile(Component owner, int mode,
346: FileFilter[] filters) {
347:
348: JFileChooser jfc = new JFileChooser();
349: jfc.setFileSelectionMode(mode);
350: jfc.setFileHidingEnabled(true);
351: jfc.setAcceptAllFileFilterUsed(true);
352: if (filters != null) {
353: for (int i = 0; i < filters.length; i++) {
354: jfc.addChoosableFileFilter(filters[i]);
355: }
356:
357: if (filters.length >= 1) {
358: jfc.setFileFilter(filters[0]);
359: }
360: }
361:
362: int result = jfc.showOpenDialog(owner);
363:
364: if (result == JFileChooser.APPROVE_OPTION) {
365: return jfc.getSelectedFile();
366: }
367:
368: return null;
369: }
370:
371: public static File getSystemFile(Component owner, int mode) {
372:
373: return getSystemFile(owner, mode, null);
374: }
375:
376: /**
377: * Consistent way to chosing a file to save to with JFileChooser.
378: * <p>
379: *
380: * @param owner to show the component relative to.
381: * @return File based on the user selection can be null.
382: */
383: public static File saveSystemFile(Component owner,
384: FileFilter[] filters) {
385:
386: JFileChooser jfc = new JFileChooser();
387: jfc.setFileHidingEnabled(true);
388: jfc.setAcceptAllFileFilterUsed(true);
389: if (filters != null) {
390: for (int i = 0; i < filters.length; i++) {
391: jfc.addChoosableFileFilter(filters[i]);
392: }
393:
394: if (filters.length >= 1) {
395: jfc.setFileFilter(filters[0]);
396: }
397: }
398:
399: int result = jfc.showSaveDialog(owner);
400:
401: if (result == JFileChooser.APPROVE_OPTION) {
402: File selection = jfc.getSelectedFile();
403: FileFilter filter = jfc.getFileFilter();
404: if (filter != null && filter instanceof ExtensionFileFilter) {
405: ExtensionFileFilter eff = (ExtensionFileFilter) filter;
406: if (!filter.accept(selection)) {
407: selection = eff.applyExtension(selection);
408: }
409: }
410:
411: if (selection.exists()) {
412: String msg = "File_Overwrite_Message"
413: + selection.getName();
414: String title = "Warning";
415: int response = JOptionPane.showConfirmDialog(owner,
416: msg, title, JOptionPane.YES_NO_OPTION);
417: if (response == JOptionPane.YES_OPTION) {
418: return selection;
419: }
420: } else {
421: try {
422: if (selection.createNewFile()) {
423: return selection;
424: }
425: } catch (IOException ioe) {
426: ioe.printStackTrace();
427: }
428: }
429:
430: }
431:
432: return null;
433: }
434:
435: public static File saveSystemFile(Component owner) {
436:
437: return saveSystemFile(owner, null);
438: }
439:
440: protected static int getLineOfOffset(int offset, Document doc)
441: throws BadLocationException {
442:
443: if (offset < 0 || doc == null) {
444: throw new BadLocationException("", -1);
445: } else if (offset > doc.getLength()) {
446: throw new BadLocationException("", doc.getLength() + 1);
447: } else {
448: Element map = doc.getDefaultRootElement();
449: return map.getElementIndex(offset);
450: }
451: }
452:
453: /**
454: * Keystroke parsing code.
455: * <p>
456: * This code is identical to the Java Keystroke parsing code with one small exception that it will interpet meta as
457: * the the menu command mask e.g. command for Mac OS X, and CTRL for linux and windows.
458: *
459: * @see KeyStroke#getKeyStroke(java.lang.String)
460: */
461: public static KeyStroke getKeyStroke(String s) {
462:
463: if (s == null || s.length() == 0) {
464: return null;
465: }
466: StringTokenizer st = new StringTokenizer(s);
467: String token;
468: int mask = 0;
469: boolean released = false;
470: boolean typed = false;
471:
472: while ((token = st.nextToken()) != null) {
473: int tokenMask = 0;
474:
475: /* if token matches a modifier keyword update mask and continue */
476:
477: for (int i = 0; (tokenMask == 0)
478: && (i < modifierKeywords.length); i++) {
479: tokenMask = modifierKeywords[i].getModifierMask(token);
480: }
481:
482: if (tokenMask != 0) {
483: mask |= tokenMask;
484: continue;
485: }
486:
487: if (token.equals("released")) {
488: released = true;
489: continue;
490: }
491: if (token.equals("pressed")) {
492: continue;
493: }
494: if (token.equals("typed")) {
495: typed = true;
496: continue;
497: }
498:
499: if (typed) {
500: if (token.length() != 1) {
501: // bogus format, should really throw.
502: return null;
503: }
504: return KeyStroke.getKeyStroke(token.charAt(0));
505: }
506:
507: /* otherwise token is the keycode name less the "VK_" prefix */
508:
509: String keycodeName = "VK_" + token;
510:
511: int keycode;
512: try {
513: keycode = KeyEvent.class.getField(keycodeName).getInt(
514: KeyEvent.class);
515: } catch (Throwable t) {
516: return null;
517: }
518:
519: return KeyStroke.getKeyStroke(keycode, mask, released);
520: }
521: return null;
522: }
523:
524: private static URL getResourceURL(Class owner, String item) {
525:
526: return owner.getResource("/org/isqlviewer/resource/"
527: .concat(item));
528: }
529:
530: private static class ModifierKeyword {
531:
532: final String keyword;
533: final int mask;
534:
535: ModifierKeyword(String keyword, int mask) {
536:
537: this .keyword = keyword;
538: this .mask = mask;
539: }
540:
541: int getModifierMask(String s) {
542:
543: return (s.equals(keyword)) ? mask : 0;
544: }
545: }
546:
547: }
|