/* * StatusBar.java - The status bar displayed at the bottom of views * :tabSize=4:indentSize=4:noTabs=false: * :folding=explicit:collapseFolds=1: * * Copyright (C) 2001, 2004 Slava Pestov * Portions copyright (C) 2001 Mike Dillon * Portions copyright (C) 2008 Matthieu Casanova * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.gjt.sp.jedit.gui; //{{{ Imports import javax.swing.border.*; import javax.swing.text.Segment; import javax.swing.*; import java.awt.event.*; import java.awt.*; import java.util.Objects; import java.util.StringTokenizer; import org.gjt.sp.jedit.textarea.*; import org.gjt.sp.jedit.*; import org.gjt.sp.jedit.gui.statusbar.StatusWidgetFactory; import org.gjt.sp.jedit.gui.statusbar.Widget; import org.gjt.sp.jedit.gui.statusbar.ToolTipLabel; import org.gjt.sp.util.*; //}}} /** The status bar used to display various information to the user. * * Currently, it is used for the following: * <ul> * <li>Displaying caret position information * <li>Displaying {@link InputHandler#readNextChar(String,String)} prompts * <li>Displaying {@link #setMessage(String)} messages * <li>Displaying I/O progress * <li>Displaying various editor settings * <li>Displaying memory status * </ul> * * @version $Id$ * @author Slava Pestov * @since jEdit 3.2pre2 */ public class StatusBar extends JPanel { //{{{ StatusBar constructor public StatusBar(View view) { super(new BorderLayout()); setName("StatusBar"); setBorder(new CompoundBorder(new EmptyBorder(4,0,0, (OperatingSystem.isMacOS() ? 18 : 0)), UIManager.getBorder("TextField.border"))); this.view = view; panel = new JPanel(new BorderLayout()); box = new Box(BoxLayout.X_AXIS); panel.add(BorderLayout.EAST,box); add(BorderLayout.CENTER,panel); MouseHandler mouseHandler = new MouseHandler(); caretStatus = new ToolTipLabel(); caretStatus.setName("caretStatus"); caretStatus.setToolTipText(jEdit.getProperty("view.status.caret-tooltip")); caretStatus.addMouseListener(mouseHandler); message = new JLabel(" "); setMessageComponent(message); modeWidget = _getWidget("mode"); foldWidget = _getWidget("fold"); encodingWidget = _getWidget("encoding"); wrapWidget = _getWidget("wrap"); indentWidget = _getWidget("indent"); multiSelectWidget = _getWidget("multiSelect"); rectSelectWidget = _getWidget("rectSelect"); overwriteWidget = _getWidget("overwrite"); lineSepWidget = _getWidget("lineSep"); taskHandler = new TaskHandler(); } //}}} //{{{ propertiesChanged() method public void propertiesChanged() { Color fg = jEdit.getColorProperty("view.status.foreground"); Color bg = jEdit.getColorProperty("view.status.background"); showCaretStatus = jEdit.getBooleanProperty("view.status.show-caret-status"); panel.setBackground(bg); panel.setForeground(fg); caretStatus.setBackground(bg); caretStatus.setForeground(fg); message.setBackground(bg); message.setForeground(fg); // retarded GTK look and feel! Font font = new JLabel().getFont(); //UIManager.getFont("Label.font"); FontMetrics fm = getFontMetrics(font); if (showCaretStatus) { panel.add(BorderLayout.WEST,caretStatus); caretStatus.setFont(font); Dimension dim = new Dimension(fm.stringWidth(caretTestStr), fm.getHeight()); caretStatus.setPreferredSize(dim); updateCaretStatus(); } else panel.remove(caretStatus); String statusBar = jEdit.getProperty("view.status"); if (!Objects.equals(currentBar, statusBar)) { box.removeAll(); StringTokenizer tokenizer = new StringTokenizer(statusBar); while (tokenizer.hasMoreTokens()) { String token = tokenizer.nextToken(); if (Character.isLetter(token.charAt(0))) { Widget widget = getWidget(token); if (widget == null) { JLabel label = new JLabel(token); label.setBackground(bg); label.setForeground(fg); box.add(label); continue; } Component c = widget.getComponent(); c.setBackground(bg); c.setForeground(fg); box.add(c); widget.update(); widget.propertiesChanged(); } else { JLabel label = new JLabel(token); label.setBackground(bg); label.setForeground(fg); box.add(label); } } currentBar = statusBar; } updateBufferStatus(); updateMiscStatus(); } //}}} //{{{ addNotify() method @Override public void addNotify() { super.addNotify(); TaskManager.instance.addTaskListener(taskHandler); } //}}} //{{{ removeNotify() method @Override public void removeNotify() { super.removeNotify(); TaskManager.instance.removeTaskListener(taskHandler); } //}}} //{{{ TaskListener implementation private class TaskHandler implements TaskListener { private final Runnable statusLineIo = new Runnable() { public void run() { // don't obscure existing message if(!currentMessageIsIO && message != null && !"".equals(message.getText().trim())) return; int requestCount = TaskManager.instance.countIoTasks(); if(requestCount == 0) { setMessageAndClear(jEdit.getProperty( "view.status.io.done")); currentMessageIsIO = true; } else if(requestCount == 1) { setMessage(jEdit.getProperty( "view.status.io-1")); currentMessageIsIO = true; } else { Object[] args = {requestCount}; setMessage(jEdit.getProperty( "view.status.io",args)); currentMessageIsIO = true; } } }; //{{{ waiting() method public void waiting(Task task) { SwingUtilities.invokeLater(statusLineIo); } //}}} //{{{ running() method public void running(Task task) { } //}}} //{{{ done() method public void done(Task task) { SwingUtilities.invokeLater(statusLineIo); } //}}} //{{{ statusUpdate() method public void statusUpdated(Task task) { } //}}} //{{{ maximumUpdated() method public void maximumUpdated(Task task) { } //}}} //{{{ valueUpdated() method public void valueUpdated(Task task) { } //}}} } //}}} //}}} //{{{ getMessage() method /** * Returns the current message. * * @return the current message * @since jEdit 4.4pre1 */ public String getMessage() { return message.getText(); } //}}} //{{{ setMessageAndClear() method /** * Show a message for a short period of time. * @param message The message * @since jEdit 3.2pre5 */ public void setMessageAndClear(String message) { setMessage(message); tempTimer = new Timer(0,new ActionListener() { public void actionPerformed(ActionEvent evt) { // so if view is closed in the meantime... if(isShowing()) setMessage(null); } }); tempTimer.setInitialDelay(10000); tempTimer.setRepeats(false); tempTimer.start(); } //}}} //{{{ setMessage() method /** * Displays a status message. * @param message the message to display, it can be null */ public void setMessage(String message) { if(tempTimer != null) { tempTimer.stop(); tempTimer = null; } setMessageComponent(this.message); if(message == null) { if(view.getMacroRecorder() != null) this.message.setText(jEdit.getProperty("view.status.recording")); else this.message.setText(" "); } else this.message.setText(message); } //}}} //{{{ setMessageComponent() method public void setMessageComponent(Component comp) { currentMessageIsIO = false; if (comp == null || messageComp == comp) { return; } messageComp = comp; panel.add(BorderLayout.CENTER, messageComp); } //}}} //{{{ updateCaretStatus() method /** Updates the status bar with information about the caret position, line number, etc */ public void updateCaretStatus() { if (showCaretStatus) { Buffer buffer = view.getBuffer(); if(!buffer.isLoaded() || /* can happen when switching buffers sometimes */ buffer != view.getTextArea().getBuffer()) { caretStatus.setText(" "); return; } JEditTextArea textArea = view.getTextArea(); int caretPosition = textArea.getCaretPosition(); int currLine = textArea.getCaretLine(); // there must be a better way of fixing this... // the problem is that this method can sometimes // be called as a result of a text area scroll // event, in which case the caret position has // not been updated yet. if(currLine >= buffer.getLineCount()) return; // hopefully another caret update will come? int start = textArea.getLineStartOffset(currLine); int dot = caretPosition - start; if(dot < 0) return; int bufferLength = buffer.getLength(); buffer.getText(start,dot,seg); int virtualPosition = StandardUtilities.getVirtualWidth(seg, buffer.getTabSize()); // for GC seg.array = null; seg.count = 0; if (jEdit.getBooleanProperty("view.status.show-caret-linenumber", true)) { buf.append(currLine + 1); buf.append(','); } if (jEdit.getBooleanProperty("view.status.show-caret-dot", true)) { buf.append(dot + 1); } if (jEdit.getBooleanProperty("view.status.show-caret-virtual", true) && virtualPosition != dot) { buf.append('-'); buf.append(virtualPosition + 1); } if (buf.length() > 0) { buf.append(' '); } if (jEdit.getBooleanProperty("view.status.show-caret-offset", true) && jEdit.getBooleanProperty("view.status.show-caret-bufferlength", true)) { buf.append('('); buf.append(caretPosition); buf.append('/'); buf.append(bufferLength); buf.append(')'); } else if (jEdit.getBooleanProperty("view.status.show-caret-offset", true)) { buf.append('('); buf.append(caretPosition); buf.append(')'); } else if (jEdit.getBooleanProperty("view.status.show-caret-bufferlength", true)) { buf.append('('); buf.append(bufferLength); buf.append(')'); } caretStatus.setText(buf.toString()); buf.setLength(0); } } //}}} //{{{ updateBufferStatus() method public void updateBufferStatus() { wrapWidget.update(); indentWidget.update(); lineSepWidget.update(); modeWidget.update(); foldWidget.update(); encodingWidget.update(); } //}}} //{{{ updateMiscStatus() method public void updateMiscStatus() { multiSelectWidget.update(); rectSelectWidget.update(); overwriteWidget.update(); } //}}} //{{{ Private members private String currentBar; private final TaskHandler taskHandler; private final View view; private final JPanel panel; private final Box box; private final ToolTipLabel caretStatus; private Component messageComp; private final JLabel message; private final Widget modeWidget; private final Widget foldWidget; private final Widget encodingWidget; private final Widget wrapWidget; private final Widget indentWidget; private final Widget multiSelectWidget; private final Widget rectSelectWidget; private final Widget overwriteWidget; private final Widget lineSepWidget; /* package-private for speed */ StringBuilder buf = new StringBuilder(); private Timer tempTimer; private boolean currentMessageIsIO; private final Segment seg = new Segment(); private boolean showCaretStatus; //}}} static final String caretTestStr = "9999,999-999 (99999999/99999999)"; //{{{ getWidget() method private Widget getWidget(String name) { if ("mode".equals(name)) return modeWidget; if ("fold".equals(name)) return foldWidget; if ("encoding".equals(name)) return encodingWidget; if ("wrap".equals(name)) return wrapWidget; if ("indent".equals(name)) return indentWidget; if ("multiSelect".equals(name)) return multiSelectWidget; if ("rectSelect".equals(name)) return rectSelectWidget; if ("overwrite".equals(name)) return overwriteWidget; if ("lineSep".equals(name)) return lineSepWidget; return _getWidget(name); } //}}} //{{{ _getWidget() method private Widget _getWidget(String name) { StatusWidgetFactory widgetFactory = (StatusWidgetFactory) ServiceManager.getService("org.gjt.sp.jedit.gui.statusbar.StatusWidgetFactory", name); if (widgetFactory == null) { return null; } return widgetFactory.getWidget(view); } //}}} //{{{ MouseHandler class private class MouseHandler extends MouseAdapter { @Override public void mouseClicked(MouseEvent evt) { Object source = evt.getSource(); if(source == caretStatus && evt.getClickCount() == 2) { view.getTextArea().showGoToLineDialog(); } } } //}}} }