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

org.smallmind.swing.text.FormulaTextField Maven / Gradle / Ivy

There is a newer version: 6.2.0
Show newest version
/*
 * Copyright (c) 2007 through 2024 David Berkman
 *
 * This file is part of the SmallMind Code Project.
 *
 * The SmallMind Code Project is free software, you can redistribute
 * it and/or modify it under either, at your discretion...
 *
 * 1) The terms of GNU Affero General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or (at
 * your option) any later version.
 *
 * ...or...
 *
 * 2) The terms of the Apache License, Version 2.0.
 *
 * The SmallMind Code Project 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 or Apache License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * and the Apache License along with the SmallMind Code Project. If not, see
 *  or .
 *
 * Additional permission under the GNU Affero GPL version 3 section 7
 * ------------------------------------------------------------------
 * If you modify this Program, or any covered work, by linking or
 * combining it with other code, such other code is not for that reason
 * alone subject to any of the requirements of the GNU Affero GPL
 * version 3.
 */
package org.smallmind.swing.text;

import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.GroupLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JToggleButton;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import org.smallmind.nutsnbolts.lang.UnknownSwitchCaseException;
import org.smallmind.nutsnbolts.util.WeakEventListenerList;
import org.smallmind.swing.ComponentUtility;

public class FormulaTextField extends JPanel implements ActionListener, ItemListener, DocumentListener {

  private enum CardState {COLLAPSED, EXPANDED}

  private static final ImageIcon COLLAPSE_ICON = new ImageIcon(ClassLoader.getSystemResource("org/smallmind/swing/system/navigate_open_16.png"));
  private static final ImageIcon EXPAND_ICON = new ImageIcon(ClassLoader.getSystemResource("org/smallmind/swing/system/navigate_close_16.png"));
  private static final ImageIcon FORMULA_ICON = new ImageIcon(ClassLoader.getSystemResource("org/smallmind/swing/system/text_formula_16.png"));
  private static final ImageIcon TEXT_ICON = new ImageIcon(ClassLoader.getSystemResource("org/smallmind/swing/system/text_16.png"));
  private final WeakEventListenerList itemListenerList = new WeakEventListenerList();
  private final WeakEventListenerList documentListenerList = new WeakEventListenerList();

  private final CardLayout cardLayout;
  private final JScrollPane expandedScrollPane;
  private final JTextArea expandedTextArea;
  private final SlimTextField collapsedTextField;
  private final JToggleButton expandedFormulaButton;
  private final JToggleButton collapsedFormulaButton;
  private final AtomicBoolean documentSensitive = new AtomicBoolean(true);
  private CardState cardState;

  public FormulaTextField () {

    this(null, false);
  }

  public FormulaTextField (boolean formula) {

    this(null, formula);
  }

  public FormulaTextField (String text) {

    this(text, 5, false);
  }

  public FormulaTextField (String text, boolean formula) {

    this(text, 5, formula);
  }

  public FormulaTextField (int rows) {

    this(null, rows, false);
  }

  public FormulaTextField (int rows, boolean formula) {

    this(null, rows, formula);
  }

  public FormulaTextField (String text, int rows) {

    this(text, rows, false);
  }

  public FormulaTextField (String text, int rows, boolean formula) {

    JPanel collapsedPanel;
    JPanel expandedPanel;
    GroupLayout collapsedGroupLayout;
    GroupLayout expandedGroupLayout;
    JButton collapseButton;
    JButton expandButton;
    int textFieldHeight;

    setLayout(cardLayout = new CardLayout());

    collapsedPanel = new JPanel();
    collapsedPanel.setLayout(collapsedGroupLayout = new GroupLayout(collapsedPanel));

    expandedPanel = new JPanel();
    expandedPanel.setLayout(expandedGroupLayout = new GroupLayout(expandedPanel));

    collapseButton = new JButton(COLLAPSE_ICON);
    collapseButton.setFocusable(false);
    collapseButton.addActionListener(this);

    expandButton = new JButton(EXPAND_ICON);
    expandButton.setFocusable(false);
    expandButton.addActionListener(this);

    collapsedFormulaButton = new JToggleButton(TEXT_ICON, formula);
    collapsedFormulaButton.setSelectedIcon(FORMULA_ICON);
    collapsedFormulaButton.setFocusable(false);
    collapsedFormulaButton.setToolTipText("evaluate as text/formula");
    collapsedFormulaButton.addItemListener(this);

    expandedFormulaButton = new JToggleButton(TEXT_ICON, formula);
    expandedFormulaButton.setSelectedIcon(FORMULA_ICON);
    expandedFormulaButton.setFocusable(false);
    expandedFormulaButton.setToolTipText("evaluate as text/formula");
    expandedFormulaButton.addItemListener(this);

    collapsedTextField = new SlimTextField(text);
    collapsedTextField.getDocument().addDocumentListener(this);

    expandedScrollPane = new JScrollPane(expandedTextArea = new JTextArea(text, rows, 0), JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
    expandedTextArea.getDocument().addDocumentListener(this);

    textFieldHeight = (int)collapsedTextField.getPreferredSize().getHeight() - 1;

    collapsedGroupLayout.setHorizontalGroup(collapsedGroupLayout.createSequentialGroup().addComponent(collapsedTextField).addComponent(expandButton, 22, 22, 22).addComponent(collapsedFormulaButton, 22, 22, 22));
    collapsedGroupLayout.setVerticalGroup(collapsedGroupLayout.createParallelGroup(GroupLayout.Alignment.LEADING).addComponent(collapsedTextField, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE).addComponent(expandButton, textFieldHeight, textFieldHeight, textFieldHeight).addComponent(collapsedFormulaButton, textFieldHeight, textFieldHeight, textFieldHeight));

    expandedGroupLayout.setHorizontalGroup(expandedGroupLayout.createSequentialGroup().addComponent(expandedScrollPane).addComponent(collapseButton, 22, 22, 22).addComponent(expandedFormulaButton, 22, 22, 22));
    expandedGroupLayout.setVerticalGroup(expandedGroupLayout.createParallelGroup(GroupLayout.Alignment.LEADING).addComponent(expandedScrollPane, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE).addComponent(collapseButton, textFieldHeight, textFieldHeight, textFieldHeight).addComponent(expandedFormulaButton, textFieldHeight, textFieldHeight, textFieldHeight));

    add(collapsedPanel, "field");
    add(expandedPanel, "area");

    setMinimumSize(new Dimension(144, ComponentUtility.getMinimumHeight(collapsedTextField)));
    setPreferredSize(new Dimension(Math.min(ComponentUtility.getPreferredWidth(collapsedTextField) + 44, Short.MAX_VALUE), ComponentUtility.getPreferredHeight(collapsedTextField)));
    setMaximumSize(new Dimension(Short.MAX_VALUE, ComponentUtility.getMaximumHeight(collapsedTextField)));

    cardState = CardState.COLLAPSED;
  }

  @Override
  public void setEnabled (boolean enabled) {

    collapsedTextField.setEnabled(enabled);
    expandedTextArea.setEnabled(enabled);
    collapsedFormulaButton.setEnabled(enabled);
    expandedFormulaButton.setEnabled(enabled);

    super.setEnabled(enabled);
  }

  public String getText () {

    return expandedTextArea.getText();
  }

  public void setText (String text) {

    switch (cardState) {
      case COLLAPSED:
        collapsedTextField.setText(text);
        break;
      case EXPANDED:
        expandedTextArea.setText(text);
        break;
      default:
        throw new UnknownSwitchCaseException(cardState.name());
    }
  }

  public boolean containsDocument (Document document) {

    return (collapsedTextField.getDocument() == document) || (expandedTextArea.getDocument() == document);
  }

  public boolean containsFormulaButton (JToggleButton toggleButton) {

    return (toggleButton == collapsedFormulaButton) || (toggleButton == expandedFormulaButton);
  }

  public boolean isFormula () {

    return expandedFormulaButton.isSelected();
  }

  @Override
  public synchronized void actionPerformed (ActionEvent actionEvent) {

    cardLayout.next(this);

    switch (cardState) {
      case COLLAPSED:
        setMinimumSize(expandedScrollPane.getMinimumSize());
        setPreferredSize(expandedScrollPane.getPreferredSize());
        setMaximumSize(expandedScrollPane.getMaximumSize());

        cardState = CardState.EXPANDED;
        break;
      case EXPANDED:
        setMinimumSize(new Dimension(ComponentUtility.getMinimumWidth(expandedScrollPane), ComponentUtility.getMinimumHeight(collapsedTextField)));
        setPreferredSize(new Dimension(ComponentUtility.getPreferredWidth(expandedScrollPane), ComponentUtility.getPreferredHeight(collapsedTextField)));
        setMaximumSize(new Dimension(ComponentUtility.getMaximumWidth(expandedScrollPane), ComponentUtility.getMaximumHeight(collapsedTextField)));

        cardState = CardState.COLLAPSED;
        break;
      default:
        throw new UnknownSwitchCaseException(cardState.name());
    }

    doLayout();
  }

  @Override
  public synchronized void itemStateChanged (ItemEvent itemEvent) {

    if (itemEvent.getSource() == collapsedFormulaButton) {
      expandedFormulaButton.setSelected(collapsedFormulaButton.isSelected());
    } else {
      collapsedFormulaButton.setSelected(expandedFormulaButton.isSelected());
    }

    for (ItemListener itemListener : itemListenerList) {
      itemListener.itemStateChanged(itemEvent);
    }
  }

  @Override
  public synchronized void insertUpdate (DocumentEvent documentEvent) {

    if (documentSensitive.get()) {
      documentSensitive.set(false);

      try {
        switch (cardState) {
          case COLLAPSED:
            expandedTextArea.getDocument().insertString(documentEvent.getOffset(), collapsedTextField.getDocument().getText(documentEvent.getOffset(), documentEvent.getLength()), null);
            break;
          case EXPANDED:
            collapsedTextField.getDocument().insertString(documentEvent.getOffset(), expandedTextArea.getDocument().getText(documentEvent.getOffset(), documentEvent.getLength()), null);
            break;
          default:
            throw new UnknownSwitchCaseException(cardState.name());
        }
      } catch (BadLocationException badLocationException) {
        throw new RuntimeException(badLocationException);
      }

      for (DocumentListener documentListener : documentListenerList) {
        documentListener.insertUpdate(documentEvent);
      }

      documentSensitive.set(true);
    }
  }

  @Override
  public synchronized void removeUpdate (DocumentEvent documentEvent) {

    if (documentSensitive.get()) {
      documentSensitive.set(false);

      try {
        switch (cardState) {
          case COLLAPSED:
            expandedTextArea.getDocument().remove(documentEvent.getOffset(), documentEvent.getLength());
            break;
          case EXPANDED:
            collapsedTextField.getDocument().remove(documentEvent.getOffset(), documentEvent.getLength());
            break;
          default:
            throw new UnknownSwitchCaseException(cardState.name());
        }
      } catch (BadLocationException badLocationException) {
        throw new RuntimeException(badLocationException);
      }

      for (DocumentListener documentListener : documentListenerList) {
        documentListener.removeUpdate(documentEvent);
      }

      documentSensitive.set(true);
    }
  }

  @Override
  public synchronized void changedUpdate (DocumentEvent documentEvent) {

    if (documentSensitive.get()) {
      documentSensitive.set(false);

      for (DocumentListener documentListener : documentListenerList) {
        documentListener.changedUpdate(documentEvent);
      }

      documentSensitive.set(true);
    }
  }

  public synchronized void addItemListener (ItemListener itemListener) {

    itemListenerList.addListener(itemListener);
  }

  public synchronized void removeItemListener (ItemListener itemListener) {

    itemListenerList.removeListener(itemListener);
  }

  public synchronized void addDocumentListener (DocumentListener documentListener) {

    documentListenerList.addListener(documentListener);
  }

  public synchronized void removeDocumentListener (DocumentListener documentListener) {

    documentListenerList.removeListener(documentListener);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy