blob: 1026a60904f519cdf2d23e3d87eec036b76ace94 [file] [log] [blame]
/*
* 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.modules.i18n;
import java.awt.Component;
import java.awt.Dialog;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.io.IOException;
import java.util.Arrays;
import javax.swing.DefaultComboBoxModel;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import org.openide.DialogDescriptor;
import org.openide.DialogDisplayer;
import org.openide.ErrorManager;
import org.openide.awt.Mnemonics;
import org.openide.cookies.SaveCookie;
import org.openide.filesystems.FileObject;
import org.openide.loaders.DataObject;
import static javax.swing.GroupLayout.Alignment.BASELINE;
import static javax.swing.GroupLayout.Alignment.TRAILING;
import static javax.swing.LayoutStyle.ComponentPlacement.RELATED;
import static javax.swing.LayoutStyle.ComponentPlacement.UNRELATED;
import org.openide.loaders.DataObjectNotFoundException;
import org.openide.util.Exceptions;
/**
* Panel which is used for customizing key-value pair (and comment also)
* encapsulated by {@code I18nString} object.
* It's used inside {@code I18nPanel}.
*
* @author Peter Zavadsky
* @see I18nString
* @see I18nPanel
*/
public class PropertyPanel extends JPanel {
/** property representing the I18String. Change is fired when the i18string changes.
* Old and new objects are not sent with the notification.
*/
public static final String PROP_STRING = "propString"; //NOI18N
/** Name for resource property. */
public static final String PROP_RESOURCE = "property_resource"; //NOI18N
/** Helper name for dummy action command. */
private static final String DUMMY_ACTION = "dont_proceed"; //NOI18N
/** Customized <code>I18nString</code>. */
protected I18nString i18nString;
/** the file for that resource should be selected **/
private FileObject file;
/** Internal flag to block handling of changes to the key jtextfield,
* which didn't originate from the user but from the code. If this is >0,
* values are just being pushed to the UI, if <=0, values are being received
* from the ui.
**/
private int internalTextChange = 0;
private String innerResourceTextContent;
/** Creates new <code>PropertyPanel</code>. */
public PropertyPanel() {
initComponents();
myInitComponents();
initAccessibility();
}
@Override
public void setEnabled(boolean ena) {
super.setEnabled(ena);
commentText.setEnabled(ena);
commentLabel.setEnabled(ena);
commentScroll.setEnabled(ena);
keyBundleCombo.setEnabled(ena);
keyLabel.setEnabled(ena);
replaceFormatButton.setEnabled(ena);
replaceFormatLabel.setEnabled(ena);
replaceFormatTextField.setEnabled(ena);
valueLabel.setEnabled(ena);
valueText.setEnabled(ena);
valueScroll.setEnabled(ena);
}
/** Seter for <code>i18nString</code> property. */
public void setI18nString(I18nString i18nString) {
this.i18nString = i18nString;
updateAllValues();
firePropertyChange(PROP_STRING, null,null);
}
/** Sets the file for that resource should be selected **/
public void setFile(FileObject fo) {
this.file = fo;
}
public FileObject getFile() {
return file;
}
/** Initializes UI values. */
void updateAllValues() {
resourceText.setText(getResourceName(i18nString.getSupport().getResourceHolder().getResource()));
innerResourceTextContent = resourceText.getText();
updateBundleKeys();
updateKey();
updateValue();
updateComment();
warningLabel.setText("");
}
/** Updates selected item of <code>keyBundleCombo</code> UI.
*/
private void updateKey() {
String key = i18nString.getKey();
if ((key == null) || !key.equals(keyBundleCombo.getSelectedItem())) {
// Trick to avoid firing key selected property change.
String oldActionCommand = keyBundleCombo.getActionCommand();
keyBundleCombo.setActionCommand(DUMMY_ACTION);
internalTextChange++;
keyBundleCombo.setSelectedItem((key != null) ? key : ""); //NOI18N
internalTextChange--;
keyBundleCombo.setActionCommand(oldActionCommand);
}
updateReplaceText();
}
/** Updates <code>valueText</code> UI.
*/
private void updateValue() {
String value = i18nString.getValue();
if (!valueText.getText().equals(value)) {
valueText.setText((value != null) ? value : ""); //NOI18N
}
updateReplaceText();
}
/** Updates <code>commentText</code> UI. */
private void updateComment() {
String comment = i18nString.getComment();
if (!commentText.getText().equals(comment)) {
commentText.setText((comment != null) ? comment : ""); //NOI18N
}
}
/** Updates <code>replaceFormatTextField</code>. */
protected void updateReplaceText() {
replaceFormatTextField.setText(i18nString.getReplaceString());
}
/** Updates <code>keyBundleCombo</code> UI. */
void updateBundleKeys() {
// Trick to avoid firing key selected property change.
String oldActionCommand = keyBundleCombo.getActionCommand();
keyBundleCombo.setActionCommand(DUMMY_ACTION);
internalTextChange++;
String[] keys = i18nString.getSupport().getResourceHolder().getAllKeys();
Arrays.sort(keys);
keyBundleCombo.setModel(new DefaultComboBoxModel(keys));
internalTextChange--;
keyBundleCombo.setActionCommand(oldActionCommand);
updateKey();
}
/** Helper method. Changes resource. */
private void changeResource(DataObject resource) {
if (resource == null) {
throw new IllegalArgumentException();
}
DataObject oldValue = i18nString.getSupport().getResourceHolder().getResource();
if ((oldValue != null) && oldValue.equals(resource)) {
return;
}
i18nString.getSupport().getResourceHolder().setResource(resource);
String newResourceValue = i18nString.getSupport().getResourceHolder()
.getValueForKey(i18nString.getKey());
if (newResourceValue != null) {
i18nString.setValue(newResourceValue);
}
updateAllValues();
firePropertyChange(PROP_RESOURCE, oldValue, resource);
I18nUtil.getOptions().setLastResource2(resource);
}
public void setResource(DataObject resource) {
if (isResourceClass(resource.getClass())) {
changeResource(resource);
}
}
private boolean isResourceClass(Class clazz) {
return Arrays.asList(
i18nString.getSupport().getResourceHolder().getResourceClasses()).contains(clazz);
}
private String getResourceName(DataObject resource) {
if (resource == null) {
return ""; //NOI18N
} else {
String name = Util.getResourceName(file, resource.getPrimaryFile(), '.', false);
return (name != null) ? name : ""; //NOI18N
}
}
private void initAccessibility() {
this.getAccessibleContext().setAccessibleDescription(
I18nUtil.getBundle().getString("ACS_PropertyPanel")); //NOI18N
valueText.getAccessibleContext().setAccessibleDescription(
I18nUtil.getBundle().getString("ACS_valueText")); //NOI18N
commentText.getAccessibleContext().setAccessibleDescription(
I18nUtil.getBundle().getString("ACS_commentText")); //NOI18N
replaceFormatButton.getAccessibleContext().setAccessibleDescription(
I18nUtil.getBundle().getString("ACS_CTL_Format")); //NOI18N
replaceFormatTextField.getAccessibleContext().setAccessibleDescription(
I18nUtil.getBundle().getString("ACS_replaceFormatTextField"));//NOI18N
browseButton.getAccessibleContext().setAccessibleDescription(
I18nUtil.getBundle().getString("ACS_CTL_BrowseButton"));//NOI18N
resourceText.getAccessibleContext().setAccessibleDescription(
I18nUtil.getBundle().getString("ACS_ResourceText")); //NOI18N
}
private void myInitComponents() {
argumentsButton.setVisible(false);
// hook the Key combobox edit-field for changes
((JTextField) keyBundleCombo.getEditor().getEditorComponent()).
getDocument().addDocumentListener(new DocumentListener() {
public void changedUpdate(DocumentEvent e) { keyBundleTextChanged();}
public void insertUpdate(DocumentEvent e) {keyBundleTextChanged();}
public void removeUpdate(DocumentEvent e) {keyBundleTextChanged();}
}
);
valueText.getDocument().addDocumentListener(new DocumentListener() {
public void changedUpdate(DocumentEvent e) { valueTextChanged();}
public void insertUpdate(DocumentEvent e) {valueTextChanged();}
public void removeUpdate(DocumentEvent e) {valueTextChanged();}
}
);
}
private void keyBundleTextChanged() {
if (internalTextChange == 0) {
String key = ((JTextField) keyBundleCombo.getEditor().getEditorComponent()).getText();
if (!key.equals(i18nString.getKey())) {
i18nString.setKey(key);
firePropertyChange(PROP_STRING, null, null);
}
}
}
private void valueTextChanged() {
i18nString.setValue(valueText.getText());
// updateValue();
firePropertyChange(PROP_STRING, null, null);
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
// <editor-fold defaultstate="collapsed" desc=" UI initialization code ">
private void initComponents() {
commentLabel = new JLabel();
commentScroll = new JScrollPane();
commentText = new JTextArea();
keyLabel = new JLabel();
valueLabel = new JLabel();
valueScroll = new JScrollPane();
valueText = new JTextArea();
keyBundleCombo = new JComboBox();
replaceFormatTextField = new JTextField();
replaceFormatLabel = new JLabel();
replaceFormatButton = new JButton();
bundleNameLabel = new JLabel();
resourceText = new JTextField();
argumentsButton = new JButton();
browseButton = new JButton();
warningLabel = new JLabel();
warningLabel.setForeground(java.awt.Color.RED);
commentLabel.setLabelFor(commentText);
Mnemonics.setLocalizedText(commentLabel, I18nUtil.getBundle().getString("LBL_Comment")); // NOI18N
commentText.setRows(2);
commentText.addFocusListener(new FocusAdapter() {
@Override
public void focusLost(FocusEvent evt) {
commentTextFocusLost(evt);
}
});
commentScroll.setViewportView(commentText);
keyLabel.setLabelFor(keyBundleCombo);
Mnemonics.setLocalizedText(keyLabel, I18nUtil.getBundle().getString("LBL_Key")); // NOI18N
valueLabel.setLabelFor(valueText);
Mnemonics.setLocalizedText(valueLabel, I18nUtil.getBundle().getString("LBL_Value")); // NOI18N
valueText.setRows(2);
valueText.addFocusListener(new FocusAdapter() {
@Override
public void focusLost(FocusEvent evt) {
valueTextFocusLost(evt);
}
});
valueScroll.setViewportView(valueText);
keyBundleCombo.setEditable(true);
keyBundleCombo.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
keyBundleComboActionPerformed(evt);
}
});
replaceFormatTextField.setColumns(35);
replaceFormatTextField.setEditable(false);
replaceFormatTextField.selectAll();
replaceFormatTextField.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent evt) {
replaceFormatTextFieldFocusGained(evt);
}
});
replaceFormatLabel.setLabelFor(replaceFormatTextField);
Mnemonics.setLocalizedText(replaceFormatLabel, I18nUtil.getBundle().getString("LBL_ReplaceFormat")); // NOI18N
Mnemonics.setLocalizedText(replaceFormatButton, I18nUtil.getBundle().getString("CTL_Format")); // NOI18N
replaceFormatButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
replaceFormatButtonActionPerformed(evt);
}
});
bundleNameLabel.setLabelFor(resourceText);
Mnemonics.setLocalizedText(bundleNameLabel, I18nUtil.getBundle().getString("LBL_BundleName")); // NOI18N
resourceText.setColumns(20);
Mnemonics.setLocalizedText(argumentsButton, I18nUtil.getBundle().getString("CTL_Arguments")); // NOI18N
Mnemonics.setLocalizedText(browseButton, I18nUtil.getBundle().getString("CTL_BrowseButton")); // NOI18N
browseButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
browseButtonActionPerformed(evt);
}
});
resourceText.addKeyListener(new java.awt.event.KeyAdapter() {
@Override
public void keyReleased(java.awt.event.KeyEvent evt) {
resourceTextKeyReleased(evt);
}
});
GroupLayout layout = new GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup()
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup()
.addComponent(bundleNameLabel)
.addComponent(valueLabel)
.addComponent(commentLabel)
.addComponent(keyLabel)
.addComponent(replaceFormatLabel))
.addPreferredGap(RELATED)
.addGroup(layout.createParallelGroup()
.addGroup(layout.createSequentialGroup()
.addComponent(resourceText)
.addPreferredGap(RELATED)
.addComponent(browseButton))
.addComponent(warningLabel)
.addComponent(keyBundleCombo,0,GroupLayout.PREFERRED_SIZE,Short.MAX_VALUE)
.addComponent(valueScroll)
.addComponent(commentScroll)
.addComponent(replaceFormatTextField)))
.addGroup(TRAILING, layout.createSequentialGroup()
.addComponent(argumentsButton)
.addPreferredGap(RELATED)
.addComponent(replaceFormatButton)))
.addContainerGap()
);
layout.setVerticalGroup(
layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(BASELINE)
.addComponent(bundleNameLabel)
.addComponent(resourceText)
.addComponent(browseButton))
.addPreferredGap(RELATED)
.addGroup(layout.createParallelGroup()
.addComponent(warningLabel))
.addPreferredGap(RELATED)
.addGroup(layout.createParallelGroup(BASELINE)
.addComponent(keyLabel)
.addComponent(keyBundleCombo))
.addPreferredGap(RELATED)
.addGroup(layout.createParallelGroup()
.addComponent(valueLabel)
.addComponent(valueScroll))
.addPreferredGap(RELATED)
.addGroup(layout.createParallelGroup()
.addComponent(commentLabel)
.addComponent(commentScroll))
.addPreferredGap(UNRELATED)
.addGroup(layout.createParallelGroup(BASELINE)
.addComponent(replaceFormatTextField)
.addComponent(replaceFormatLabel))
.addPreferredGap(RELATED)
.addGroup(layout.createParallelGroup(BASELINE)
.addComponent(replaceFormatButton)
.addComponent(argumentsButton))
.addContainerGap()
);
}// </editor-fold>
private void resourceTextKeyReleased(java.awt.event.KeyEvent evt) {
if (!innerResourceTextContent.equals(resourceText.getText())
&& resourceText.getText().trim().length() != 0) {
String bundlePath = resourceText.getText()
.replaceAll("[.]", "/") //NOI18N
.concat(".properties"); //NOI18N
FileObject resourceFO = Util.getResource(file, bundlePath);
if ((resourceFO != null) && resourceFO.isValid() && !resourceFO.isVirtual()) {
try {
setResource(DataObject.find(resourceFO));
warningLabel.setText(""); //NOI18N
} catch (DataObjectNotFoundException ex) {
Exceptions.printStackTrace(ex);
}
} else {
warningLabel.setText(
I18nUtil.getBundle().getString("LBL_InvalidBundle") //NOI18N
+ bundlePath); //NOI18N
setEmptyResource();
}
} else {
warningLabel.setText(""); //NOI18N
if ("".equals(resourceText.getText())) {
setEmptyResource();
}
}
}
private void setEmptyResource() {
DataObject oldValue = i18nString.getSupport().getResourceHolder().getResource();
if (oldValue != null) {
SaveCookie save = oldValue.getCookie(SaveCookie.class);
if (save!=null) {
try {
save.save();
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
}
}
i18nString.getSupport().getResourceHolder().setResource(null);
firePropertyChange(PROP_RESOURCE, oldValue, null);
}
private void browseButtonActionPerformed(ActionEvent evt) {
if(file == null) {
return; // Panel has not initialised fully yet.
}
ResourceHolder rh = i18nString.getSupport().getResourceHolder();
DataObject template;
try {
Class resourceClass = rh.getResourceClasses()[0];
template = rh.getTemplate(resourceClass);
if(template==null) { // #175881
throw new NullPointerException(
"A template is not created for the class " +
resourceClass +
"\nPlease, check an implementation of " +
"the createTemplate(Class) method " +
"for the following resource holder:\n" +
rh.toString()); // NOI18N
}
} catch (IOException ex) {
ErrorManager.getDefault().notify(ex);
return;
}
catch (NullPointerException ex) {
ErrorManager.getDefault().notify(ex);
return;
}
DataObject resource =
SelectorUtils.selectOrCreateBundle(
file,
template,
i18nString.getSupport().getResourceHolder().getResource());
//DataObject resource = SelectorUtils.selectBundle(this.project, file);
if (resource != null) {
changeResource(resource);
warningLabel.setText(""); //NOI18N
}
}
private void replaceFormatTextFieldFocusGained(FocusEvent evt) {
// Accessibility
replaceFormatTextField.selectAll();
}
private boolean isReplaceFormatValid(String replaceFormat) {
I18nString i18nReplaceCheck = (I18nString) i18nString.clone();
i18nReplaceCheck.setReplaceFormat(replaceFormat);
return i18nReplaceCheck.getReplaceString() != null;
}
private void replaceFormatButtonActionPerformed(ActionEvent evt) {
final Dialog[] dialogs = new Dialog[1];
final HelpStringCustomEditor customPanel = new HelpStringCustomEditor(
i18nString.getReplaceFormat(),
I18nUtil.getReplaceFormatItems(),
I18nUtil.getReplaceHelpItems(),
I18nUtil.getBundle().getString("LBL_ReplaceCodeFormat"),
I18nUtil.PE_REPLACE_CODE_HELP_ID);
final DialogDescriptor dd = new DialogDescriptor(
customPanel,
I18nUtil.getBundle().getString("LBL_ReplaceStringFormatEditor"),//NOI18N
true,
DialogDescriptor.OK_CANCEL_OPTION,
DialogDescriptor.OK_OPTION,
new ActionListener() {
public void actionPerformed(ActionEvent ev) {
final Object source = ev.getSource();
if (source == DialogDescriptor.OK_OPTION) {
String newText = (String) customPanel.getPropertyValue();
if (!newText.equals(replaceFormatTextField.getText())) {
i18nString.setReplaceFormat(newText);
updateReplaceText();
firePropertyChange(PROP_STRING, null, null);
// Reset option as well.
I18nUtil.getOptions().setReplaceJavaCode(newText);
}
dialogs[0].setVisible(false);
dialogs[0].dispose();
} else if (source == DialogDescriptor.CANCEL_OPTION) {
dialogs[0].setVisible(false);
dialogs[0].dispose();
}
}
});
dd.setValid(isReplaceFormatValid(i18nString.getReplaceFormat()));
// look for ComboBox to create a hook, which would disable/enable
// the OK button based on validity of replace format string
for (Component c : customPanel.getComponents()) {
if (c instanceof JComboBox) {
try {
((JTextField) ((JComboBox) c).getEditor().getEditorComponent()).getDocument().addDocumentListener(new DocumentListener() {
public void insertUpdate(DocumentEvent e) {
try {
dd.setValid(isReplaceFormatValid(e.getDocument().getText(0, e.getDocument().getLength())));
} catch (BadLocationException ex) {
Exceptions.printStackTrace(ex);
}
}
public void removeUpdate(DocumentEvent e) {
try {
dd.setValid(isReplaceFormatValid(e.getDocument().getText(0, e.getDocument().getLength())));
} catch (BadLocationException ex) {
Exceptions.printStackTrace(ex);
}
}
public void changedUpdate(DocumentEvent e) {
try {
dd.setValid(isReplaceFormatValid(e.getDocument().getText(0, e.getDocument().getLength())));
} catch (BadLocationException ex) {
Exceptions.printStackTrace(ex);
}
}
});
} catch (Exception e) {
Exceptions.printStackTrace(e);
}
break;
}
}
dialogs[0] = DialogDisplayer.getDefault().createDialog(dd);
dialogs[0].setVisible(true);
}
private void keyBundleComboActionPerformed(ActionEvent evt) {
if (DUMMY_ACTION.equals(evt.getActionCommand())) {
return;
}
String key = (String)keyBundleCombo.getSelectedItem();
i18nString.setKey(key);
updateKey();
String value = i18nString.getSupport().getResourceHolder().getValueForKey(key);
if (value != null) {
i18nString.setValue(value);
updateValue();
}
String comment = i18nString.getSupport().getResourceHolder().getCommentForKey(key);
if (comment != null) {
i18nString.setComment(comment);
updateComment();
}
firePropertyChange(PROP_STRING, null, null);
}
private void commentTextFocusLost(FocusEvent evt) {
i18nString.setComment(commentText.getText());
updateComment();
firePropertyChange(PROP_STRING, null, null);
}
private void valueTextFocusLost(FocusEvent evt) {
valueTextChanged();
}
protected JButton argumentsButton;
private JButton browseButton;
private JLabel commentLabel;
private JScrollPane commentScroll;
private JTextArea commentText;
private JLabel bundleNameLabel;
private JComboBox keyBundleCombo;
private JLabel keyLabel;
private JButton replaceFormatButton;
private JLabel replaceFormatLabel;
private JTextField replaceFormatTextField;
private JTextField resourceText;
private JLabel valueLabel;
private JScrollPane valueScroll;
private JTextArea valueText;
private JLabel warningLabel;
}