001: package net.sourceforge.squirrel_sql.client.session;
002:
003: import java.awt.event.ActionEvent;
004: import java.awt.event.MouseAdapter;
005: import java.awt.event.MouseEvent;
006: import java.awt.event.MouseListener;
007:
008: import javax.swing.Action;
009: import javax.swing.JMenu;
010: import javax.swing.JMenuItem;
011: import javax.swing.SwingUtilities;
012:
013: import net.sourceforge.squirrel_sql.client.IApplication;
014: import net.sourceforge.squirrel_sql.client.session.action.ViewObjectAtCursorInObjectTreeAction;
015: import net.sourceforge.squirrel_sql.fw.gui.GUIUtils;
016: import net.sourceforge.squirrel_sql.fw.gui.TextPopupMenu;
017: import net.sourceforge.squirrel_sql.fw.id.IIdentifier;
018: import net.sourceforge.squirrel_sql.fw.id.IntegerIdentifierFactory;
019:
020: /*
021: * Copyright (C) 2001-2003 Colin Bell
022: * colbell@users.sourceforge.net
023: *
024: * This library is free software; you can redistribute it and/or
025: * modify it under the terms of the GNU Lesser General Public
026: * License as published by the Free Software Foundation; either
027: * version 2.1 of the License, or (at your option) any later version.
028: *
029: * This library is distributed in the hope that it will be useful,
030: * but WITHOUT ANY WARRANTY; without even the implied warranty of
031: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
032: * Lesser General Public License for more details.
033: *
034: * You should have received a copy of the GNU Lesser General Public
035: * License along with this library; if not, write to the Free Software
036: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
037: */
038: public abstract class BaseSQLEntryPanel implements ISQLEntryPanel {
039: protected final static String LINE_SEPARATOR = "\n";
040:
041: protected final static String SQL_STMT_SEP = LINE_SEPARATOR
042: + LINE_SEPARATOR;
043:
044: public static final IntegerIdentifierFactory ENTRY_PANEL_IDENTIFIER_FACTORY = new IntegerIdentifierFactory();
045: private IIdentifier _entryPanelIdentifier;
046:
047: private TextPopupMenu _textPopupMenu;
048: private IApplication _app;
049:
050: private MouseListener _sqlEntryMouseListener = new MyMouseListener();
051:
052: protected BaseSQLEntryPanel(IApplication app) {
053: _entryPanelIdentifier = ENTRY_PANEL_IDENTIFIER_FACTORY
054: .createIdentifier();
055: _textPopupMenu = new TextPopupMenu();
056: _app = app;
057:
058: SwingUtilities.invokeLater(new Runnable() {
059: public void run() {
060: getTextComponent().addMouseListener(
061: _sqlEntryMouseListener);
062: }
063: });
064:
065: }
066:
067: public IIdentifier getIdentifier() {
068: return _entryPanelIdentifier;
069: }
070:
071: public String getSQLToBeExecuted() {
072: String sql = getSelectedText();
073: if (sql == null || sql.trim().length() == 0) {
074: sql = getText();
075: int[] bounds = getBoundsOfSQLToBeExecuted();
076:
077: if (bounds[0] >= bounds[1]) {
078: sql = "";
079: } else {
080: sql = sql.substring(bounds[0], bounds[1]).trim();
081: }
082: }
083: return sql != null ? sql : "";
084: }
085:
086: public int[] getBoundsOfSQLToBeExecuted() {
087: int[] bounds = new int[2];
088: bounds[0] = getSelectionStart();
089: bounds[1] = getSelectionEnd();
090:
091: if (bounds[0] == bounds[1]) {
092: bounds = getSqlBoundsBySeparatorRule(getCaretPosition());
093: }
094:
095: return bounds;
096: }
097:
098: /**
099: * The non selection separator is two new lines. Two new lines with white spaces in between
100: * is counted as separator too.
101: * @return
102: */
103: private int[] getSqlBoundsBySeparatorRule(int iCaretPos) {
104: int[] bounds = new int[2];
105:
106: String sql = getText();
107:
108: bounds[0] = lastIndexOfStateSep(sql, iCaretPos);
109: bounds[1] = indexOfStateSep(sql, iCaretPos);
110:
111: return bounds;
112:
113: }
114:
115: private static int indexOfStateSep(String sql, int pos) {
116: int ix = pos;
117:
118: int newLinteCount = 0;
119: for (;;) {
120: if (sql.length() == ix) {
121: return sql.length();
122: }
123:
124: if (false == Character.isWhitespace(sql.charAt(ix))) {
125: newLinteCount = 0;
126: }
127:
128: if ('\n' == sql.charAt(ix)) {
129: ++newLinteCount;
130: if (2 == newLinteCount) {
131: return ix - 1;
132: }
133: }
134:
135: ++ix;
136: }
137: }
138:
139: private static int lastIndexOfStateSep(String sql, int pos) {
140: int ix = pos;
141:
142: int newLinteCount = 0;
143: for (;;) {
144:
145: if (ix == sql.length()) {
146: if (ix == 0) {
147: return ix;
148: } else {
149: ix--;
150: }
151: }
152:
153: if (false == Character.isWhitespace(sql.charAt(ix))) {
154: newLinteCount = 0;
155: }
156:
157: if ('\n' == sql.charAt(ix)) {
158: ++newLinteCount;
159: if (2 == newLinteCount) {
160: return ix + newLinteCount;
161: }
162: }
163:
164: if (0 == ix) {
165: return 0 + newLinteCount;
166: }
167:
168: --ix;
169: }
170: }
171:
172: public void moveCaretToPreviousSQLBegin() {
173: String sql = getText();
174:
175: int iCaretPos = getCaretPosition() - 1;
176: int iLastIndex = sql.lastIndexOf(SQL_STMT_SEP, iCaretPos);
177:
178: if (-1 == iLastIndex) {
179: return;
180: }
181:
182: iLastIndex = sql.lastIndexOf(SQL_STMT_SEP, iLastIndex
183: - getWhiteSpaceCountBackwards(iLastIndex, sql));
184:
185: if (-1 == iLastIndex) {
186: iLastIndex = 0;
187: }
188:
189: char c = sql.charAt(iLastIndex);
190: while (Character.isWhitespace(c) && iLastIndex < sql.length()) {
191: ++iLastIndex;
192: c = sql.charAt(iLastIndex);
193:
194: }
195: setCaretPosition(iLastIndex);
196: }
197:
198: private int getWhiteSpaceCountBackwards(int iStartIx, String sql) {
199:
200: int count = 0;
201: while (0 < iStartIx
202: && Character.isWhitespace(sql.charAt(iStartIx))) {
203: --iStartIx;
204: ++count;
205: }
206:
207: return count;
208:
209: }
210:
211: public void moveCaretToNextSQLBegin() {
212: String sql = getText();
213:
214: int iCaretPos = getCaretPosition();
215: int iNextIndex = sql.indexOf(SQL_STMT_SEP, iCaretPos);
216:
217: if (-1 == iNextIndex) {
218: return;
219: }
220:
221: while (iNextIndex < sql.length()
222: && Character.isWhitespace(sql.charAt(iNextIndex))) {
223: ++iNextIndex;
224: }
225:
226: if (iNextIndex < sql.length()) {
227: setCaretPosition(iNextIndex);
228: }
229: }
230:
231: public void selectCurrentSql() {
232: int[] boundsOfSQLToBeExecuted = getSqlBoundsBySeparatorRule(getCaretPosition());
233:
234: if (boundsOfSQLToBeExecuted[0] != boundsOfSQLToBeExecuted[1]) {
235: setSelectionStart(boundsOfSQLToBeExecuted[0]);
236: setSelectionEnd(boundsOfSQLToBeExecuted[1]);
237: }
238: }
239:
240: /**
241: * Add a hierarchical menu to the SQL Entry Area popup menu.
242: *
243: * @param menu The menu that will be added.
244: *
245: * @throws IllegalArgumentException
246: * Thrown if <TT>null</TT> <TT>Menu</TT> passed.
247: */
248: public void addToSQLEntryAreaMenu(JMenu menu) {
249: if (menu == null) {
250: throw new IllegalArgumentException("Menu == null");
251: }
252:
253: _textPopupMenu.add(menu);
254: }
255:
256: /**
257: * Add an <TT>Action</TT> to the SQL Entry Area popup menu.
258: *
259: * @param action The action to be added.
260: *
261: * @throws IllegalArgumentException
262: * Thrown if <TT>null</TT> <TT>Action</TT> passed.
263: */
264: public JMenuItem addToSQLEntryAreaMenu(Action action) {
265: if (action == null) {
266: throw new IllegalArgumentException("Action == null");
267: }
268:
269: return _textPopupMenu.add(action);
270: }
271:
272: /**
273: * @see ISQLEntryPanel#setUndoActions(javax.swing.Action, javax.swing.Action)
274: */
275: public void setUndoActions(Action undo, Action redo) {
276: _textPopupMenu.addSeparator();
277:
278: JMenuItem buf;
279:
280: buf = addToSQLEntryAreaMenu(undo);
281: _app.getResources().configureMenuItem(undo, buf);
282:
283: buf = addToSQLEntryAreaMenu(redo);
284: _app.getResources().configureMenuItem(redo, buf);
285:
286: _textPopupMenu.addSeparator();
287: }
288:
289: public void dispose() {
290: _textPopupMenu.dispose();
291: }
292:
293: private final class MyMouseListener extends MouseAdapter {
294: public void mousePressed(MouseEvent evt) {
295: if (evt.isPopupTrigger()) {
296: displayPopupMenu(evt);
297: }
298: }
299:
300: public void mouseReleased(MouseEvent evt) {
301: if (evt.isPopupTrigger()) {
302: displayPopupMenu(evt);
303: }
304: }
305:
306: /**
307: * This provides the feature which is like in Eclipse when you control
308: * click on an identifier it takes you to the file that contains the
309: * identifier (method, class, etc) definition. Similarly, ctrl-clicking
310: * on an identifier in the SQL editor will invoke the view object at
311: * cursor in object tree action.
312: */
313: @Override
314: public void mouseClicked(MouseEvent e) {
315: if (e.isControlDown()) {
316:
317: final Action a = _app.getActionCollection().get(
318: ViewObjectAtCursorInObjectTreeAction.class);
319: GUIUtils.processOnSwingEventThread(new Runnable() {
320: public void run() {
321: a
322: .actionPerformed(new ActionEvent(this ,
323: 1,
324: "ViewObjectAtCursorInObjectTreeAction"));
325: }
326: });
327: }
328: }
329:
330: private void displayPopupMenu(MouseEvent evt) {
331: _textPopupMenu.setTextComponent(getTextComponent());
332: _textPopupMenu.show(evt.getComponent(), evt.getX(), evt
333: .getY());
334: }
335: }
336:
337: }
|