All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.netbeans.editor.StatusBar Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.netbeans.editor;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Insets;
import java.awt.GridBagLayout;
import java.awt.GridBagConstraints;
import java.awt.FontMetrics;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.List;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.prefs.PreferenceChangeEvent;
import java.util.prefs.PreferenceChangeListener;
import java.util.prefs.Preferences;
import javax.swing.Action;
import javax.swing.JPanel;
import javax.swing.JLabel;
import javax.swing.Timer;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
import javax.swing.event.ChangeListener;
import javax.swing.event.ChangeEvent;
import javax.swing.text.JTextComponent;
import javax.swing.text.Caret;
import javax.swing.text.Document;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.UIManager;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import org.netbeans.api.editor.EditorRegistry;
import org.netbeans.api.editor.mimelookup.MimeLookup;
import org.netbeans.api.editor.settings.FontColorNames;
import org.netbeans.api.editor.settings.FontColorSettings;
import org.netbeans.api.editor.settings.SimpleValueNames;
import org.netbeans.modules.editor.lib2.EditorPreferencesDefaults;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle;
import org.openide.util.WeakListeners;

/**
 * Status bar support.
 * 
* By default the status bar is hidden and a global IDE's status bar is used. * It is only visible if the editor window is undocked. * * @author Miloslav Metelka * @version 1.00 */ public class StatusBar implements PropertyChangeListener, DocumentListener { /** * Besides line|column display also caret offset in status bar. */ // -J-Dorg.netbeans.editor.caret.offset.level=FINE private static final Logger CARET_OFFSET_LOG = Logger.getLogger("org.netbeans.editor.caret.offset"); public static final String CELL_MAIN = "main"; // NOI18N public static final String CELL_POSITION = "position"; // NOI18N public static final String CELL_TYPING_MODE = "typing-mode"; // NOI18N public static final String INSERT_LOCALE = "status-bar-insert"; // NOI18N public static final String OVERWRITE_LOCALE = "status-bar-overwrite"; // NOI18N private static final String[] POS_MAX_STRINGS = new String[] { "99999:999/9999:9999" }; // NOI18N private static final String[] POS_MAX_STRINGS_OFFSET = new String[] { "99999:999/9999:9999 <99999999>" }; // NOI18N private static final Insets NULL_INSETS = new Insets(0, 0, 0, 0); static final Border CELL_BORDER = BorderFactory.createCompoundBorder( BorderFactory.createCompoundBorder( BorderFactory.createMatteBorder(1,0,0,0,UIManager.getDefaults().getColor("control")), // NOI18N BorderFactory.createCompoundBorder( BorderFactory.createMatteBorder(0,0,1,1,UIManager.getDefaults().getColor("controlHighlight")), // NOI18N BorderFactory.createLineBorder(UIManager.getDefaults().getColor("controlDkShadow")) // NOI18N ) ), BorderFactory.createEmptyBorder(0, 2, 0, 2) ); private static Map cellName2GlobalCell = new HashMap(); public static void setGlobalCell(String cellName, JLabel globalCell) { cellName2GlobalCell.put(cellName, globalCell); } protected EditorUI editorUI; /** The status bar panel into which the cells are added. */ private JPanel panel; private boolean visible; private List cellList = new ArrayList(); private Caret caret; private CaretListener caretL; private int caretDelay; private boolean overwriteModeDisplayed; private String insText; private String ovrText; private String caretPositionLocaleString; private String insertModeLocaleString; private String overwriteModeLocaleString; private Preferences prefs = null; private final PreferenceChangeListener prefsListener = new PreferenceChangeListener() { public void preferenceChange(PreferenceChangeEvent evt) { // #50073 SwingUtilities.invokeLater(new Runnable(){ public void run(){ refreshPanel(); } }); if (evt == null || SimpleValueNames.STATUS_BAR_CARET_DELAY.equals(evt.getKey())) { caretDelay = prefs.getInt(SimpleValueNames.STATUS_BAR_CARET_DELAY, EditorPreferencesDefaults.defaultStatusBarCaretDelay); if (caretL != null) { caretL.setDelay(caretDelay); } } } }; private PreferenceChangeListener weakListener = null; static final long serialVersionUID =-6266183959929157349L; public StatusBar(EditorUI editorUI) { this.editorUI = editorUI; caretDelay = 10; caretL = new CaretListener(caretDelay); ResourceBundle bundle = NbBundle.getBundle(BaseKit.class); insText = bundle.getString(INSERT_LOCALE); ovrText = bundle.getString(OVERWRITE_LOCALE); caretPositionLocaleString = bundle.getString("status-bar-caret-position"); //NOI18N insertModeLocaleString = bundle.getString("status-bar-insert-mode"); //NOI18N overwriteModeLocaleString = bundle.getString("status-bar-overwrite-mode"); //NOI18N synchronized (editorUI.getComponentLock()) { // if component already installed in EditorUI simulate installation JTextComponent component = editorUI.getComponent(); if (component != null) { propertyChange(new PropertyChangeEvent(editorUI, EditorUI.COMPONENT_PROPERTY, null, component)); } editorUI.addPropertyChangeListener(this); } } private void documentUndo(DocumentEvent evt) { Utilities.runInEventDispatchThread(new Runnable() { public void run() { // Clear the main cell setText(CELL_MAIN, ""); } }); } public void insertUpdate(DocumentEvent evt) { if (evt.getType() == DocumentEvent.EventType.REMOVE) { // undo documentUndo(evt); } } public void removeUpdate(DocumentEvent evt) { if (evt.getType() == DocumentEvent.EventType.INSERT) { // undo documentUndo(evt); } } public void changedUpdate(DocumentEvent evt) { } protected JPanel createPanel() { return new JPanel(new GridBagLayout()); } public boolean isVisible() { return visible; } public void setVisible(boolean v) { if (v != visible) { visible = v; if (panel != null || visible) { if (visible) { // need to refresh first refreshPanel(); } // fix for issue 13842 if (SwingUtilities.isEventDispatchThread()) { getPanel().setVisible(visible); } else { SwingUtilities.invokeLater( new Runnable() { public void run() { getPanel().setVisible(visible); } } ); } } } } /** * Update values into global cells. * @since 1.37 */ public void updateGlobal() { // Update all cell mappings for (Map.Entry e : cellName2GlobalCell.entrySet()) { if (CELL_MAIN.equals(e.getKey())) { // Do not sync main cell into global panel continue; } String s = getText(e.getKey()); e.getValue().setText(s); } } public final JPanel getPanel() { if (panel == null) { panel = createPanel(); initPanel(); } return panel; } protected void initPanel() { JLabel cell = addCell(CELL_POSITION, CARET_OFFSET_LOG.isLoggable(Level.FINE) ? POS_MAX_STRINGS_OFFSET : POS_MAX_STRINGS); cell.setHorizontalAlignment(SwingConstants.CENTER); cell.addMouseListener(new MouseAdapter() { public @Override void mouseClicked(MouseEvent e) { if (e.getClickCount() == 2) { JTextComponent jtc = editorUI.getComponent(); if (jtc != null) { BaseKit kit = Utilities.getKit(jtc); if (kit != null) { Action a = kit.getActionByName(org.netbeans.editor.ext.ExtKit.gotoAction); if (a != null) { a.actionPerformed(new ActionEvent(jtc, 0, null)); } } } } } }); addCell(CELL_TYPING_MODE, new String[] { insText, ovrText }).setHorizontalAlignment( SwingConstants.CENTER); setText(CELL_TYPING_MODE, insText); addCell(CELL_MAIN, null); } public void propertyChange(PropertyChangeEvent evt) { String propName = evt.getPropertyName(); if (EditorUI.COMPONENT_PROPERTY.equals(propName)) { if (prefs != null && weakListener != null) { prefs.removePreferenceChangeListener(weakListener); } JTextComponent component = (JTextComponent)evt.getNewValue(); if (component != null) { // just installed component.addPropertyChangeListener(this); caret = component.getCaret(); if (caret != null) { caret.addChangeListener(caretL); } Document doc = component.getDocument(); if (doc != null) { doc.addDocumentListener(this); } String mimeType = org.netbeans.lib.editor.util.swing.DocumentUtilities.getMimeType(component); prefs = MimeLookup.getLookup(mimeType).lookup(Preferences.class); weakListener = WeakListeners.create(PreferenceChangeListener.class, prefsListener, prefs); prefs.addPreferenceChangeListener(weakListener); prefsListener.preferenceChange(null); refreshPanel(); } else { // just deinstalled component = (JTextComponent)evt.getOldValue(); component.removePropertyChangeListener(this); caret = component.getCaret(); if (caret != null) { caret.removeChangeListener(caretL); } Document doc = component.getDocument(); if (doc != null) { doc.removeDocumentListener(this); } } } else if ("caret".equals(propName)) { // NOI18N if (caret != null) { caret.removeChangeListener(caretL); } caret = (Caret)evt.getNewValue(); if (caret != null) { caret.addChangeListener(caretL); } } else if ("document".equals(propName)) { // NOI18N Document old = (Document)evt.getOldValue(); Document cur = (Document)evt.getNewValue(); if (old != null) { old.removeDocumentListener(this); } if (cur != null) { cur.addDocumentListener(this); } } // Refresh the panel after each property-change if (EditorUI.OVERWRITE_MODE_PROPERTY.equals(propName)) { caretL.actionPerformed(null); // refresh immediately } else { // not overwrite mode change caretL.stateChanged(null); } } /** #86272: Applies given coloring on cell in special way, stick with default * LF color for status bar cells when foreColor or backColor of Coloring is null */ private void applyColoring(Cell cell, Coloring coloring) { coloring.apply(cell); if (coloring.getForeColor() == null) { cell.setForeground(cell.getDefaultForeground()); } if (coloring.getBackColor() == null) { cell.setBackground(cell.getDefaultBackground()); } } public int getCellCount() { return cellList.size(); } public JLabel addCell(String name, String[] widestStrings) { return addCell(-1, name, widestStrings); } public JLabel addCell(int i, String name, String[] widestStrings) { Cell c = new Cell(name, widestStrings); addCellImpl(i, c); return c; } public void addCustomCell(int i, JLabel c) { addCellImpl(i, c); } private void addCellImpl(int i, JLabel c) { synchronized (cellList) { ArrayList newCellList = new ArrayList(cellList); int cnt = newCellList.size(); if (i < 0 || i > cnt) { i = cnt; } newCellList.add(i, c); cellList = newCellList; updateCellBorders(i); } refreshPanel(); } /** Manages cell borders so that left, right and inner cells have properly * assigned borders for various LFs. Borders are special, installed by * core into UIManager maps. */ private void updateCellBorders(int addedIndex) { int cellCount = getCellCount(); Border innerBorder = (Border)UIManager.get("Nb.Editor.Status.innerBorder"); //NOI18N Border leftBorder = (Border)UIManager.get("Nb.Editor.Status.leftBorder"); //NOI18N Border rightBorder = (Border)UIManager.get("Nb.Editor.Status.rightBorder"); //NOI18N Border onlyOneBorder = (Border)UIManager.get("Nb.Editor.Status.onlyOneBorder"); //NOI18N if ((innerBorder == null) || (leftBorder == null) || (rightBorder == null) || onlyOneBorder == null) { // don't modify borders at all if some is not available return; } if (cellCount == 1) { // only one cell ((JLabel)cellList.get(0)).setBorder(onlyOneBorder); return; } if (addedIndex == 0) { // added as first, updates second ((JLabel)cellList.get(0)).setBorder(leftBorder); JLabel second = (JLabel)cellList.get(1); second.setBorder(cellCount == 2 ? rightBorder : innerBorder); } else if (addedIndex == cellCount - 1) { // added as last, updates previous ((JLabel)cellList.get(cellCount - 1)).setBorder(rightBorder); JLabel previous = (JLabel)cellList.get(cellCount - 2); previous.setBorder(cellCount == 2 ? leftBorder : innerBorder); } else { // cell added inside ((JLabel)cellList.get(addedIndex)).setBorder(innerBorder); } } public JLabel getCellByName(String name) { Iterator i = cellList.iterator(); while (i.hasNext()) { JLabel c = (JLabel)i.next(); if (name.equals(c.getName())) { return c; } } return null; } public String getText(String cellName) { JLabel cell = getCellByName(cellName); return (cell != null) ? cell.getText() : null; } public void setText(String cellName, String text) { setText(cellName, text, null); } private static Coloring getColoring(String mimeType, String highlight) { FontColorSettings fcs = MimeLookup.getLookup(mimeType).lookup(FontColorSettings.class); AttributeSet attribs = fcs == null ? null : fcs.getFontColors(highlight); return attribs == null ? null : Coloring.fromAttributeSet(attribs); } public void setBoldText(String cellName, String text) { JTextComponent jtc = editorUI.getComponent(); setText(cellName, text, jtc != null ? getColoring(org.netbeans.lib.editor.util.swing.DocumentUtilities.getMimeType(jtc), FontColorNames.STATUS_BAR_BOLD_COLORING) : null); } public void setText(String cellName, String text, Coloring extraColoring) { setText(cellName, text, extraColoring, 1); } public void setText(String cellName, String text, Coloring extraColoring, int importance) { JLabel cell = getCellByName(cellName); if (cell != null) { cell.setText(text); if (visible) { JTextComponent jtc = editorUI.getComponent(); Coloring c = jtc != null ? getColoring( org.netbeans.lib.editor.util.swing.DocumentUtilities.getMimeType(jtc), FontColorNames.STATUS_BAR_COLORING ) : null; if (c != null && extraColoring != null) { c = extraColoring.apply(c); } else if (c == null) { c = extraColoring; } if (CELL_POSITION.equals(cellName)){ cell.setToolTipText(caretPositionLocaleString); } else if (CELL_TYPING_MODE.equals(cellName)) { cell.setToolTipText(insText.equals(text)? insertModeLocaleString : overwriteModeLocaleString); } else { cell.setToolTipText(text == null || text.length() == 0 ? null : text); } if (c != null && cell instanceof Cell) { applyColoring((Cell) cell, c); } } else { // Status bar not visible => use global status bar if possible JLabel globalCell = cellName2GlobalCell.get(cellName); if (globalCell != null) { if (CELL_MAIN.equals(cellName)) { globalCell.putClientProperty("importance", importance); } globalCell.setText(text); } } } } /** * Set text into main cell with a given importance. * * @param text non-null text to be displayed. * @param importance positive integer having */ public void setText(String text, int importance) { JTextComponent jtc = editorUI.getComponent(); setText(CELL_MAIN, text, jtc != null ? getColoring( org.netbeans.lib.editor.util.swing.DocumentUtilities.getMimeType(jtc), FontColorNames.STATUS_BAR_BOLD_COLORING) : null, importance); } /* Refresh the whole panel by removing all the components * and adding only those that appear in the cell-list. */ private void refreshPanel() { if (isVisible()) { // refresh only if visible // Apply coloring to all cells Iterator it = cellList.iterator(); while (it.hasNext()) { JLabel c = (JLabel)it.next(); JTextComponent jtc = editorUI.getComponent(); if (c instanceof Cell && jtc != null /*#141362 Check editorUI.getComponent() not null*/) { Coloring col = getColoring( org.netbeans.lib.editor.util.swing.DocumentUtilities.getMimeType(jtc), FontColorNames.STATUS_BAR_COLORING ); if (col != null) { applyColoring((Cell) c, col); } } } // Layout cells GridBagConstraints gc = new GridBagConstraints(); gc.gridx = GridBagConstraints.RELATIVE; gc.gridwidth = 1; gc.gridheight = 1; it = cellList.iterator(); while (it.hasNext()) { JLabel c = (JLabel)it.next(); boolean main = CELL_MAIN.equals(c.getName()); if (main) { gc.fill = GridBagConstraints.HORIZONTAL; gc.weightx = 1.0; } getPanel().add(c, gc); if (main) { gc.fill = GridBagConstraints.NONE; gc.weightx = 0; } } } } class CaretListener implements ChangeListener, ActionListener { Timer timer; CaretListener(int delay) { timer = new Timer(delay, new WeakTimerListener(this)); timer.setRepeats(false); } void setDelay(int delay) { timer.setInitialDelay(delay); } public void stateChanged(ChangeEvent evt) { timer.restart(); } public void actionPerformed(ActionEvent evt) { Caret c = caret; JTextComponent component = editorUI.getComponent(); // Also check whether the component is last focused since when undocking an editor // all the components' carets fire this listener and so invalid component's // data would get displayed in the global status bar. if (component != null && component == EditorRegistry.lastFocusedComponent()) { if (c != null) { BaseDocument doc = Utilities.getDocument(component); if (doc != null && doc.getDefaultRootElement().getElementCount()>0) { int pos = c.getDot(); String s = Utilities.debugPosition(doc, pos, ":"); if (CARET_OFFSET_LOG.isLoggable(Level.FINE)) { // Possibly add caret offset info s += " <" + pos + ">"; // NOI18N } int countOfSelectedChars = component.getSelectionEnd() - component.getSelectionStart(); final boolean hasSelection = countOfSelectedChars > 0; if (hasSelection) { try { //count of selected lines int lineEnd = Utilities.getLineOffset(doc, component.getSelectionEnd()); int lineStart = Utilities.getLineOffset(doc, component.getSelectionStart()); s += "/" + (lineEnd - lineStart + 1); } catch (BadLocationException ex) { } //count of selected characters s += ":" + countOfSelectedChars; } //rows:cols/countRows:countCols setText(CELL_POSITION, s); } } Boolean b = (Boolean)editorUI.getProperty(EditorUI.OVERWRITE_MODE_PROPERTY); boolean om = (b != null && b.booleanValue()); if (om != overwriteModeDisplayed) { overwriteModeDisplayed = om; setText(CELL_TYPING_MODE, overwriteModeDisplayed ? ovrText : insText); } } } } static class Cell extends JLabel { Dimension maxDimension; String[] widestStrings; private final Color defaultBackground; private final Color defaultForeground; static final long serialVersionUID =-2554600362177165648L; Cell(String name, String[] widestStrings) { setName(name); setBorder(CELL_BORDER); setOpaque(true); this.widestStrings = widestStrings; this.defaultBackground = getBackground(); this.defaultForeground = getForeground(); updateSize(); } private void updateSize() { Font f = getFont(); if (maxDimension == null) { maxDimension = new Dimension(); } if (f != null) { Border b = getBorder(); Insets ins = (b != null) ? b.getBorderInsets(this) : NULL_INSETS; FontMetrics fm = getFontMetrics(f); String text = this.getText(); int mw = text == null ? 0 : fm.stringWidth(text); maxDimension.height = fm.getHeight() + ins.top + ins.bottom; if (widestStrings != null) { for (int i = 0; i < widestStrings.length; i++) { String widestString = widestStrings[i]; if (widestString == null){ continue; } mw = Math.max(mw, fm.stringWidth(widestString)); } } maxDimension.width = mw + ins.left + ins.right; } } public @Override Dimension getPreferredSize() { if (maxDimension == null) { maxDimension = new Dimension(); } return new Dimension(maxDimension); } public @Override Dimension getMinimumSize(){ if (maxDimension == null) { maxDimension = new Dimension(); } return new Dimension(maxDimension); } public @Override void setFont(Font f) { super.setFont(f); updateSize(); } @Override public void setBorder(Border border) { super.setBorder(border); updateSize(); } /** Returns default foreground color of the cell, for current LF and theme. * @return Default foreground color */ public Color getDefaultForeground () { Color color = (Color) UIManager.get("Label.foreground"); //NOI18N return color != null ? color : defaultForeground; } /** Returns default background color of the cell, for current LF and theme. * @return Default background color */ public Color getDefaultBackground () { Color color = UIManager.getColor("NbEditorStatusBar.background"); //NOI18N if( null == color ) color = (Color) UIManager.get("Label.background"); //NOI18N return color != null ? color : defaultBackground; } } public static final class StatusBarFactory implements SideBarFactory { public JComponent createSideBar(JTextComponent target) { return Utilities.getEditorUI(target).getStatusBar().getPanel(); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy