/* * Copyright (c) 2010, SQL Power Group Inc. * * This file is part of Wabit. * * Wabit 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 3 of the License, or * (at your option) any later version. * * Wabit 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, see <http://www.gnu.org/licenses/>. */ package ca.sqlpower.swingui.object; import java.awt.Color; import java.awt.Component; import java.awt.Graphics; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import javax.swing.text.Element; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; import ca.sqlpower.object.SPVariableHelper; import ca.sqlpower.swingui.DataEntryPanelBuilder; public class VariableLabel extends JLabel { public final static String VAR_VALUE = "sqlp-variable-value"; public final static String UUID = "sqlp-variable-uuid"; public final static String VARIABLE_CHAR = "\u0001"; private final static int MARGIN = 10; private String variableDef; /** * Builds a label object that represents a variable * in a swing {@link Document} object. * @param variableDef The variable key, including namespace and * default value, but excluding the "${}" characters. */ public VariableLabel( final String variableDef, final SPVariableHelper helper) { super(" " + SPVariableHelper.getKey(variableDef) + " > "); super.setForeground(super.getBackground()); this.variableDef = variableDef; setAlignmentY(BOTTOM); setHorizontalAlignment(JLabel.CENTER); } @Override public void paint(Graphics g) { Color colorBackup = g.getColor(); g.setColor(Color.BLUE); g.fillRoundRect( 0, 0, getWidth(), getHeight(), MARGIN, MARGIN ); g.setColor(Color.WHITE); g.drawString(" " + SPVariableHelper.getKey(variableDef) + " > ", 0, getHeight() - 4); g.setColor(colorBackup); } @Override public String toString() { return formatVariable(variableDef); } public void setVariableDef(String variableDef) { this.variableDef = variableDef; this.setText(" " + SPVariableHelper.getKey(VariableLabel.this.variableDef) + " > "); } /** * Helper method to insert and register a styled label in a AWT Document * * @param variableDef * The variable unique key, including namespace, name and default * value, but excluding ${} markers. * @param helper * The helper to use if this variable needs to be modified. * @param target * The target {@link Document} into which to insert this * variable. * @param insertPosition * The position at which to insert the variable. * @param dialogOwner * The owner of the dialog that will be displayed if the * variable is edited. * @throws BadLocationException * If the position supplied is not a valid position within the * target document. */ public static void insertLabel( final String variableDef, final SPVariableHelper helper, final Document target, final int insertPosition, final Component dialogOwner) throws BadLocationException { insertLabel(variableDef, helper, target, insertPosition, dialogOwner, true); } /** * Helper method to insert and register a styled label in a AWT Document * * @param variableDef * The variable unique key, including namespace, name and default * value, but excluding ${} markers. * @param helper * The helper to use if this variable needs to be modified. * @param target * The target {@link Document} into which to insert this * variable. * @param insertPosition * The position at which to insert the variable. * @param dialogOwner * The owner of the dialog that will be displayed if the variable * is edited. * @param showVariablesPanelOnClick * The determinant of whether to show a {@link VariablesPanel} * when the label is clicked on. * @throws BadLocationException * If the position supplied is not a valid position within the * target document. */ public static void insertLabel( final String variableDef, final SPVariableHelper helper, final Document target, final int insertPosition, final Component dialogOwner, boolean showVariablesPanelOnClick) throws BadLocationException { final String uuid = java.util.UUID.randomUUID().toString(); final SimpleAttributeSet varStyle = new SimpleAttributeSet(); varStyle.addAttribute(VAR_VALUE, variableDef); varStyle.addAttribute(UUID, uuid); final VariableLabel labelToInsert = new VariableLabel(variableDef, helper); StyleConstants.setComponent(varStyle, labelToInsert); target.insertString(insertPosition, VARIABLE_CHAR, varStyle); if (showVariablesPanelOnClick) { // Open the editor if the user clicks on the label labelToInsert.addMouseListener(new MouseListener() { public void mouseReleased(MouseEvent e) { } public void mousePressed(MouseEvent e) { } public void mouseExited(MouseEvent e) { } public void mouseEntered(MouseEvent e) { } public void mouseClicked(MouseEvent e) { VariablesPanel vp = new VariablesPanel( helper, new VariableInserter() { public void insert(String variable) { // We don't know where the var is now, but we can search it // by UUID attribute. for (int i = 0; i < target.getLength(); i++) { try { if (getCharacterElement(target, i).getAttributes().isDefined(UUID) && getCharacterElement(target, i).getAttributes().getAttribute(UUID).equals(uuid) && target.getText(i, 1).equals(VARIABLE_CHAR)) { // Gotcha! target.remove(i, 1); String newVariableDef = variable.replaceFirst("\\$", "").replaceFirst("\\{", "").replaceFirst("\\}", ""); insertLabel(newVariableDef, helper, target, i, dialogOwner); } } catch (BadLocationException e) { throw new AssertionError(e); } } } }, variableDef); JDialog dialog = DataEntryPanelBuilder.createDataEntryPanelDialog( vp, dialogOwner, "Edit variable", "Update"); dialog.setVisible(true); } }); } } /** * Takes a {@link Document} object and switches all the * variables labels for textual representations of the variables. * @param target */ public static void removeLabels(Document target) { try { for (int i = 0; i < target.getLength(); i++) { if (getCharacterElement(target, i).getAttributes().isDefined(VAR_VALUE) && target.getText(i, 1).equals(VARIABLE_CHAR)) { String varValue = (String)getCharacterElement(target, i).getAttributes().getAttribute(VAR_VALUE); target.remove(i, 1); target.insertString( i, formatVariable(varValue), null); } } } catch (BadLocationException e) { throw new RuntimeException(e); } } /** * Takes a {@link Document} object and removes all * variables labels and returns the textual representation * of the contents with variables represented as ${bla}. * @param target The document to analyze * @return The string contents. */ public static String getText(Document target) { try { StringBuffer sb = new StringBuffer(""); for (int i = 0; i < target.getLength(); i++) { if (getCharacterElement(target, i).getAttributes().isDefined(VAR_VALUE) && target.getText(i, 1).equals(VARIABLE_CHAR)) { String varValue = (String)getCharacterElement(target, i).getAttributes().getAttribute(VAR_VALUE); sb.append(formatVariable(varValue)); } else { sb.append(target.getText(i, 1)); } } return sb.toString(); } catch (BadLocationException e) { throw new RuntimeException(e); } } /** * This helper method parses the contents of an AWT Document and inserts * pretty variables labels in it. * * @param helper * The var helper to use if the variables are modified after * being inserted. * @param target * The Document into which to perform the substitution * @param dialogOwner * The owner of the dialog that will be displayed if the * variables are edited. */ public static void insertLabels( SPVariableHelper helper, Document target, Component dialogOwner) { insertLabels(helper, target, dialogOwner, true); } /** * This helper method parses the contents of an AWT Document and inserts * pretty variables labels in it. * * @param helper * The var helper to use if the variables are modified after * being inserted. * @param target * The Document into which to perform the substitution * @param dialogOwner * The owner of the dialog that will be displayed if the * variables are edited. * @param showVariablesPanelOnClick * The determinant of whether to show a {@link VariablesPanel} * when the labels are clicked on. */ public static void insertLabels( SPVariableHelper helper, Document target, Component dialogOwner, boolean showVariablesPanelOnClick) { try { while (true) { String text = target.getText(0, target.getLength()); int indexStart = text.indexOf("${"); int indexEnd = text.indexOf("}"); if (indexStart == -1 || indexEnd == -1) { break; } String var = text.substring(indexStart, indexEnd + 1); var = var.replaceFirst("\\$", "").replaceFirst("\\{", "").replaceFirst("\\}", ""); target.remove(indexStart, indexEnd - indexStart + 1); insertLabel(var, helper, target, indexStart, dialogOwner, showVariablesPanelOnClick); } } catch (BadLocationException e) { throw new AssertionError(e); } } /** * Helper method to insert and register a styled label in * a AWT Document that will be rendered by the Picollo library. * @param variableDef The variable unique key, including namespace, * name and default value, but exclusing ${} markers. * @param target The terget {@link Document} into which to insert * this variable. * @param insertPosition The position at whch to insert the variable. * @throws BadLocationException If the position supplied is not a valid * position within the target document. */ public static void insertLabelForPicollo( final String variableDef, final Document target, final int insertPosition) throws BadLocationException { final SimpleAttributeSet varStyle = new SimpleAttributeSet(); // These attributes will help us convert to/from the nice label format varStyle.addAttribute(VAR_VALUE, variableDef); // Now we add attributes to make picollo display the variables in a nice way StyleConstants.setBackground(varStyle, Color.BLUE); StyleConstants.setForeground(varStyle, Color.WHITE); StyleConstants.setBold(varStyle, true); target.insertString(insertPosition, " " + SPVariableHelper.getKey(variableDef) + " ", varStyle); } /** * This helper method parses the contents of an AWT Document * which will be rendered by the Picollo library * and inserts pretty variables labels in it. * @param target The Document into which to perform the * substitution */ public static void insertLabelsForPicollo(Document target) { try { while (true) { String text = target.getText(0, target.getLength()); int indexStart = text.indexOf("${"); int indexEnd = text.indexOf("}"); if (indexStart == -1 || indexEnd == -1) { break; } String var = text.substring(indexStart, indexEnd + 1); var = var.replaceFirst("\\$", "").replaceFirst("\\{", "").replaceFirst("\\}", ""); target.remove(indexStart, indexEnd - indexStart + 1); insertLabelForPicollo(var, target, indexStart); } } catch (BadLocationException e) { throw new AssertionError(e); } } /** * Takes a {@link Document} object who was crafted to be displayed * by the Picollo library and switches all the * variables labels for textual representations of the variables. * @param target */ public static void removeLabelsForPicollo(Document target) { try { for (int i = 0; i < target.getLength(); i++) { if (getCharacterElement(target, i).getAttributes().isDefined(VAR_VALUE)) { // We must figure out the end of the var string. int endIndex = i; while (endIndex < target.getLength() && getCharacterElement(target, endIndex).getAttributes().isDefined(VAR_VALUE)) { endIndex++; } String varValue = (String)getCharacterElement(target, i).getAttributes().getAttribute(VAR_VALUE); target.remove(i, endIndex - i); target.insertString( i, formatVariable(varValue), null); } } } catch (BadLocationException e) { throw new RuntimeException(e); } } /** * Formats a variable name into a form where the variable looks unresolved. * For example, var -> ${var}. This method works in reverse of * {@link #stripVariable(String)}. * * @param variableName * The variable name. * @return The formatted unresolved variable. */ public static String formatVariable(String variableName) { return "${" + variableName + "}"; } /** * Strips the characters from an unresolved variable to get the variable * name from it. For example, ${var} -> var. This method works in reverse of * {@link #formatVariable(String)}. * * @param formattedVariableName * The unresolved variable. * @return The stripped variable name. */ public static String stripVariable(String formattedVariableName) { int indexStart = formattedVariableName.indexOf("${"); int indexEnd = formattedVariableName.indexOf("}"); if (indexStart == -1 || indexEnd == -1 || indexStart > indexEnd) { return formattedVariableName; } String var = formattedVariableName.substring(indexStart, indexEnd + 1); return var.replaceFirst("\\$", "").replaceFirst("\\{", "").replaceFirst("\\}", ""); } public static Element getCharacterElement(Document doc, int pos) { Element e = null; for (e = doc.getDefaultRootElement(); ! e.isLeaf(); ) { int index = e.getElementIndex(pos); e = e.getElement(index); } return e; } }