001: /*
002: * DwStatusBar.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.sql;
013:
014: import java.awt.BorderLayout;
015: import java.awt.Color;
016: import java.awt.Dimension;
017: import java.awt.EventQueue;
018: import java.awt.FlowLayout;
019: import java.awt.Font;
020: import java.awt.FontMetrics;
021: import java.awt.Insets;
022: import java.awt.event.ActionEvent;
023: import java.awt.event.ActionListener;
024: import java.awt.event.MouseEvent;
025: import java.awt.event.MouseListener;
026: import java.text.DecimalFormat;
027: import java.text.DecimalFormatSymbols;
028: import java.text.SimpleDateFormat;
029:
030: import javax.swing.BorderFactory;
031: import javax.swing.JLabel;
032: import javax.swing.JPanel;
033: import javax.swing.JTextField;
034: import javax.swing.SwingConstants;
035: import javax.swing.Timer;
036: import javax.swing.border.Border;
037: import javax.swing.border.CompoundBorder;
038: import javax.swing.border.EmptyBorder;
039: import javax.swing.border.LineBorder;
040:
041: import workbench.gui.WbSwingUtilities;
042: import workbench.gui.components.DividerBorder;
043: import workbench.gui.components.TextComponentMouseListener;
044: import workbench.gui.components.WbTextLabel;
045: import workbench.interfaces.EditorStatusbar;
046: import workbench.interfaces.EventDisplay;
047: import workbench.interfaces.StatusBar;
048: import workbench.resource.ResourceMgr;
049: import workbench.resource.Settings;
050: import workbench.util.EventNotifier;
051: import workbench.util.NotifierEvent;
052: import workbench.util.NumberStringCache;
053: import workbench.util.StringUtil;
054:
055: /**
056: *
057: * @author support@sql-workbench.net
058: */
059: public class DwStatusBar extends JPanel implements StatusBar,
060: EditorStatusbar, ActionListener, EventDisplay, MouseListener {
061: private JTextField tfRowCount;
062:
063: protected WbTextLabel tfStatus;
064:
065: private JTextField tfMaxRows;
066: private String readyMsg;
067: private JTextField tfTimeout;
068: private WbTextLabel execTime;
069: private JLabel editorStatus;
070: private JPanel infoPanel;
071:
072: private static final int BAR_HEIGHT = 22;
073: private static final int FIELD_HEIGHT = 18;
074: private DecimalFormat numberFormatter;
075: private SimpleDateFormat timeFormatter = new SimpleDateFormat(
076: "m'm' ss's'");
077:
078: private int timerInterval = Settings.getInstance().getIntProperty(
079: "workbench.gui.execution.timer.interval", 1000);
080: private int timerDelay = Settings.getInstance().getIntProperty(
081: "workbench.gui.execution.timer.interval", 1000);
082: private final boolean showTimer = Settings.getInstance()
083: .getBoolProperty("workbench.gui.execution.timer.enabled",
084: true);
085: private long timerStarted;
086: private Timer executionTimer;
087: private boolean timerRunning;
088: private ActionListener notificationHandler;
089: private JLabel notificationLabel;
090: private String editorLinePrefix;
091: private String editorColPrefix;
092:
093: public DwStatusBar() {
094: this (false, false);
095: }
096:
097: public DwStatusBar(boolean showTimeout, boolean showEditorStatus) {
098: Dimension d = new Dimension(40, FIELD_HEIGHT);
099: this .tfRowCount = new JTextField();
100: this .tfMaxRows = new JTextField(6);
101: this .tfMaxRows.setEditable(true);
102: this .tfMaxRows.setMaximumSize(d);
103: this .tfMaxRows.setMargin(new Insets(0, 2, 0, 2));
104: this .tfMaxRows.setText("0");
105: this .tfMaxRows.setName("maxrows");
106: this .tfMaxRows.setToolTipText(ResourceMgr
107: .getDescription("TxtMaxRows"));
108: this .tfMaxRows.setHorizontalAlignment(SwingConstants.RIGHT);
109: this .tfMaxRows
110: .addMouseListener(new TextComponentMouseListener());
111:
112: Border b = BorderFactory.createCompoundBorder(new LineBorder(
113: Color.LIGHT_GRAY, 1), new EmptyBorder(1, 1, 1, 1));
114: this .tfMaxRows.setBorder(b);
115:
116: this .setLayout(new BorderLayout());
117:
118: this .setMaximumSize(new Dimension(32768, BAR_HEIGHT));
119: this .setMinimumSize(new Dimension(80, BAR_HEIGHT));
120: this .setPreferredSize(null);
121: tfRowCount.setEditable(false);
122: tfRowCount.setHorizontalAlignment(JTextField.RIGHT);
123: tfRowCount.setBorder(WbSwingUtilities.EMPTY_BORDER);
124: tfRowCount.setDisabledTextColor(Color.BLACK);
125: tfRowCount.setMargin(new Insets(0, 15, 0, 10));
126: tfRowCount.setMinimumSize(d);
127: tfRowCount.setPreferredSize(null);
128: tfRowCount.setAutoscrolls(false);
129: tfRowCount.setEnabled(false);
130:
131: this .tfStatus = new WbTextLabel();
132: tfStatus.setMaximumSize(new Dimension(32768, FIELD_HEIGHT));
133: tfStatus.setMinimumSize(new Dimension(80, FIELD_HEIGHT));
134: tfStatus.setPreferredSize(null);
135:
136: this .add(tfStatus, BorderLayout.CENTER);
137:
138: JPanel p = new JPanel(new FlowLayout(FlowLayout.RIGHT, 0, 0));
139: p.setBorder(WbSwingUtilities.EMPTY_BORDER);
140: p.setMaximumSize(new Dimension(300, FIELD_HEIGHT));
141:
142: this .infoPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 2,
143: 0));
144: setBorder(WbSwingUtilities.EMPTY_BORDER);
145: p.add(infoPanel);
146:
147: this .execTime = new WbTextLabel();
148: execTime.setHorizontalAlignment(SwingConstants.RIGHT);
149: this .execTime.setToolTipText(ResourceMgr
150: .getString("MsgTotalSqlTime"));
151:
152: Font f = execTime.getFont();
153: FontMetrics fm = null;
154: if (f != null)
155: fm = execTime.getFontMetrics(f);
156:
157: if (showTimer) {
158: this .executionTimer = new Timer(timerInterval, this );
159: }
160:
161: if (showEditorStatus) {
162: this .editorStatus = new JLabel();
163: this .editorStatus
164: .setHorizontalAlignment(SwingConstants.CENTER);
165: int ew = (fm == null ? 85 : fm.stringWidth("L:999 C:999"));
166: d = new Dimension(ew + 4, FIELD_HEIGHT);
167: editorStatus.setMinimumSize(d);
168: this .editorStatus.setBorder(new CompoundBorder(
169: new DividerBorder(DividerBorder.LEFT),
170: new EmptyBorder(0, 3, 0, 3)));
171: this .editorStatus.setToolTipText(ResourceMgr
172: .getDescription("LblEditorStatus"));
173: p.add(editorStatus);
174: this .editorColPrefix = ResourceMgr
175: .getString("LblEditorPosCol");
176: this .editorLinePrefix = ResourceMgr
177: .getString("LblEditorPosLine");
178: }
179:
180: b = new CompoundBorder(new DividerBorder(
181: DividerBorder.LEFT_RIGHT), new EmptyBorder(0, 3, 0, 3));
182: int width = (fm == null ? 100 : fm.stringWidth("000000000000s"));
183: d = new Dimension(width + 4, FIELD_HEIGHT);
184: execTime.setPreferredSize(d);
185: execTime.setMaximumSize(d);
186: execTime.setBorder(b);
187: p.add(execTime);
188:
189: if (showTimeout) {
190: JLabel l = new JLabel(" "
191: + ResourceMgr.getString("LblQueryTimeout") + " ");
192: //l.setBorder(new DividerBorder(DividerBorder.LEFT));
193: p.add(l);
194: this .tfTimeout = new JTextField(3);
195: this .tfTimeout.setBorder(b);
196: this .tfTimeout.setMargin(new Insets(0, 2, 0, 2));
197: this .tfTimeout.setToolTipText(ResourceMgr
198: .getDescription("LblQueryTimeout"));
199: this .tfTimeout.setHorizontalAlignment(SwingConstants.RIGHT);
200: this .tfTimeout
201: .addMouseListener(new TextComponentMouseListener());
202: l.setToolTipText(this .tfTimeout.getToolTipText());
203: p.add(this .tfTimeout);
204: }
205: JLabel l = new JLabel(" " + ResourceMgr.getString("LblMaxRows")
206: + " ");
207: l.setToolTipText(this .tfRowCount.getToolTipText());
208: p.add(l);
209: p.add(tfMaxRows);
210: p.add(tfRowCount);
211: this .add(p, BorderLayout.EAST);
212:
213: this .readyMsg = ResourceMgr.getString("MsgReady");
214: this .clearStatusMessage();
215:
216: numberFormatter = DwStatusBar.createTimingFormatter();
217: EventNotifier.getInstance().addEventDisplay(this );
218: }
219:
220: public static final DecimalFormat createTimingFormatter() {
221: DecimalFormatSymbols symb = new DecimalFormatSymbols();
222: String sep = Settings.getInstance().getProperty(
223: "workbench.gui.timining.decimal", ".");
224: symb.setDecimalSeparator(sep.charAt(0));
225: DecimalFormat numberFormatter = new DecimalFormat("0.#s", symb);
226: numberFormatter.setMaximumFractionDigits(2);
227: return numberFormatter;
228: }
229:
230: public void setReadyMsg(String aMsg) {
231: if (aMsg == null) {
232: this .readyMsg = StringUtil.EMPTY_STRING;
233: } else {
234: this .readyMsg = aMsg;
235: }
236: }
237:
238: public void clearExecutionTime() {
239: this .execTime.setText("");
240: this .execTime.repaint();
241: }
242:
243: public void setEditorLocation(int line, int column) {
244: if (this .editorStatus == null)
245: return;
246: StringBuilder text = new StringBuilder(20);
247: text.append(editorLinePrefix);
248: text.append(NumberStringCache.getNumberString(line));
249: text.append(' ');
250: text.append(editorColPrefix);
251: text.append(NumberStringCache.getNumberString(column));
252: this .editorStatus.setText(text.toString());
253: }
254:
255: public void executionStart() {
256: if (!showTimer)
257: return;
258: timerStarted = System.currentTimeMillis();
259: executionTimer.setInitialDelay(timerDelay);
260: executionTimer.setDelay(timerInterval);
261: timerRunning = true;
262: executionTimer.start();
263: }
264:
265: public void executionEnd() {
266: if (!showTimer)
267: return;
268: timerRunning = false;
269: executionTimer.stop();
270: }
271:
272: private String formatDuration(long millis) {
273: if (millis < 1000)
274: return "0s";
275: else if (millis <= 60000)
276: return Long.toString((millis / 1000)) + "s";
277: else
278: return timeFormatter.format(new java.util.Date(millis));
279: }
280:
281: public void actionPerformed(ActionEvent e) {
282: if (!timerRunning)
283: return;
284: long time = System.currentTimeMillis() - timerStarted;
285: this .execTime.setText(formatDuration(time));
286: }
287:
288: public void setExecutionTime(long millis) {
289: final long oneMinute = (1000 * 60);
290: final long oneHour = oneMinute * 60;
291:
292: if (timerRunning)
293: executionEnd();
294:
295: // Access to the formatters is not synchronized
296: // as they setExecutionTime() will not be called
297: // from multiple Threads
298: if (millis < oneMinute) {
299: double time = (millis / 1000.0);
300: this .execTime.setText(numberFormatter.format(time));
301: } else if (millis < oneHour) {
302: this .execTime.setText(timeFormatter
303: .format(new java.util.Date(millis)));
304: } else {
305: long hours = (millis / oneHour);
306: long rest = millis - (hours * oneHour);
307: this .execTime.setText(Long.toString(hours) + "h "
308: + timeFormatter.format(new java.util.Date(rest)));
309: }
310: this .execTime.repaint();
311: }
312:
313: public void setRowcount(int start, int end, int count) {
314: final StringBuilder s = new StringBuilder(20);
315: if (count > 0) {
316: // for some reason the layout manager does not leave enough
317: // space to the left of the text, so we'll add some space here
318: s.append(' ');
319: s.append(NumberStringCache.getNumberString(start));
320: s.append('-');
321: s.append(NumberStringCache.getNumberString(end));
322: s.append('/');
323: s.append(NumberStringCache.getNumberString(count));
324: }
325: tfRowCount.setText(s.toString());
326: refresh();
327: }
328:
329: private Runnable refresher = new Runnable() {
330: public void run() {
331: validate();
332: repaint();
333: }
334: };
335:
336: protected void refresh() {
337: EventQueue.invokeLater(refresher);
338: }
339:
340: public void clearRowcount() {
341: tfRowCount.setText("");
342: refresh();
343: }
344:
345: public String getText() {
346: return this .tfStatus.getText();
347: }
348:
349: /**
350: * Show a message in the status panel.
351: *
352: * This method might be called from within a background thread, so we
353: * need to make sure the actual setText() stuff is called on the AWT
354: * thread in order to update the GUI correctly.
355: * @see DwStatusBar#setStatusMessage(String)
356: */
357: public void setStatusMessage(final String aMsg) {
358: if (aMsg == null)
359: return;
360: tfStatus.setText(aMsg);
361: }
362:
363: public void forcePaint() {
364: tfStatus.forcePaint();
365: }
366:
367: /**
368: * Clears the status bar by displaying the default message.
369: */
370: public void clearStatusMessage() {
371: this .setStatusMessage(this .readyMsg);
372: }
373:
374: public void setQueryTimeout(int timeout) {
375: if (this .tfTimeout != null) {
376: this .tfTimeout.setText(Integer.toString(timeout));
377: }
378: }
379:
380: public int getQueryTimeout() {
381: if (this .tfTimeout == null)
382: return 0;
383: return StringUtil.getIntValue(this .tfTimeout.getText(), 0);
384: }
385:
386: public void setMaxRows(int max) {
387: this .tfMaxRows.setText(Integer.toString(max));
388: }
389:
390: public int getMaxRows() {
391: if (this .tfMaxRows == null)
392: return 0;
393: return StringUtil.getIntValue(this .tfMaxRows.getText(), 0);
394: }
395:
396: public void selectMaxRowsField() {
397: this .tfMaxRows.selectAll();
398: this .tfMaxRows.requestFocusInWindow();
399: }
400:
401: public void showAlert(NotifierEvent evt) {
402: if (this .notificationHandler != null) {
403: this .removeAlert();
404: }
405: this .infoPanel.removeAll();
406: this .notificationHandler = evt.getHandler();
407: this .notificationLabel = new JLabel(ResourceMgr.getImage(evt
408: .getIconKey()));
409: notificationLabel.setText(null);
410: notificationLabel.setToolTipText(evt.getTooltip());
411: notificationLabel.setIconTextGap(0);
412: this .notificationLabel.addMouseListener(this );
413: this .infoPanel.add(notificationLabel);
414: WbSwingUtilities.repaintLater(this );
415: }
416:
417: public void removeAlert() {
418: this .infoPanel.removeAll();
419: this .notificationLabel.removeMouseListener(this );
420: this .notificationHandler = null;
421: WbSwingUtilities.repaintLater(this );
422: }
423:
424: public void mouseClicked(MouseEvent e) {
425: if (e.getSource() != this .notificationLabel)
426: return;
427: if (this .notificationHandler == null)
428: return;
429:
430: if (e.getButton() == MouseEvent.BUTTON1) {
431: ActionEvent evt = new ActionEvent(this , -1,
432: "notifierClicked");
433: this .notificationHandler.actionPerformed(evt);
434: }
435: }
436:
437: public void mousePressed(MouseEvent e) {
438: }
439:
440: public void mouseReleased(MouseEvent e) {
441: }
442:
443: public void mouseEntered(MouseEvent e) {
444: }
445:
446: public void mouseExited(MouseEvent e) {
447: }
448: }
|