blob: d70cb84b17931746bc93c5d824b98f992b7267eb [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.properties;
import java.awt.Component;
import java.awt.datatransfer.Transferable;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import javax.swing.Action;
import javax.swing.SwingUtilities;
import org.openide.DialogDescriptor;
import org.openide.filesystems.FileUtil;
import org.openide.nodes.CookieSet;
import org.openide.nodes.Node;
import org.openide.nodes.NodeTransfer;
import org.openide.NotifyDescriptor;
import org.openide.DialogDisplayer;
import org.openide.actions.CopyAction;
import org.openide.actions.CutAction;
import org.openide.actions.DeleteAction;
import org.openide.actions.EditAction;
import org.openide.actions.FileSystemAction;
import org.openide.actions.NewAction;
import org.openide.actions.OpenAction;
import org.openide.actions.PasteAction;
import org.openide.actions.PropertiesAction;
import org.openide.actions.SaveAsTemplateAction;
import org.openide.actions.ToolsAction;
import org.openide.filesystems.FileStateInvalidException;
import org.openide.filesystems.FileStatusEvent;
import org.openide.filesystems.FileStatusListener;
import org.openide.filesystems.FileSystem;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.StatusDecorator;
import org.openide.util.actions.SystemAction;
import org.openide.util.datatransfer.NewType;
import org.openide.util.datatransfer.PasteType;
import org.openide.util.HelpCtx;
import org.openide.util.NbBundle;
/**
* Node representing a single properties file.
*
* @see PropertiesDataNode
* @author Ian Formanek
* @author Marian Petras
*/
public final class PropertiesLocaleNode extends FileEntryNode
implements CookieSet.Factory,
Node.Cookie {
/** Icon base for the <code>PropertiesDataNode</code> node. */
private static final String LOCALE_ICON_BASE = "org/netbeans/modules/properties/propertiesLocale.gif"; // NOI18N
private FileStatusListener fsStatusListener;
/** Creates a new PropertiesLocaleNode for the given locale-specific file */
public PropertiesLocaleNode (PropertiesFileEntry fe) {
super(fe, fe.getChildren());
setDisplayName(Util.getLocaleLabel(fe));
setIconBaseWithExtension(LOCALE_ICON_BASE);
setShortDescription(messageToolTip());
// the node uses lookup based on CookieSet from PropertiesFileEntry
CookieSet cookieSet = fe.getCookieSet();
cookieSet.add(PropertiesOpen.class, this);
cookieSet.add(fe);
cookieSet.add(fe.getDataObject());
cookieSet.add(this);
fsStatusListener = new FSListener();
try {
FileSystem fs = fe.getFile().getFileSystem();
fs.addFileStatusListener(FileUtil.weakFileStatusListener(fsStatusListener, fs));
} catch (FileStateInvalidException ex) {
}
}
private class FSListener implements FileStatusListener, Runnable {
public void annotationChanged(FileStatusEvent ev) {
if (ev.isNameChange() && ev.hasChanged(getFileEntry().getFile())) {
SwingUtilities.invokeLater(this);
}
}
public void run() {
fireDisplayNameChange(null, null);
}
}
/** Implements <code>CookieSet.Factory</code> interface method. */
@SuppressWarnings("unchecked")
public <T extends Node.Cookie> T createCookie(Class<T> clazz) {
if(clazz.isAssignableFrom(PropertiesOpen.class)) {
return (T) ((PropertiesDataObject) getFileEntry().getDataObject()).getOpenSupport();
} else {
return null;
}
}
/** Lazily initialize set of node's actions.
* Overrides superclass method.
*
* @return array of actions for this node
*/
@Override
protected SystemAction[] createActions () {
return new SystemAction[] {
SystemAction.get(EditAction.class),
SystemAction.get(OpenAction.class),
null,
SystemAction.get(CutAction.class),
SystemAction.get(CopyAction.class),
SystemAction.get(PasteAction.class),
null,
SystemAction.get(DeleteAction.class),
SystemAction.get(LangRenameAction.class),
null,
SystemAction.get(NewAction.class),
SystemAction.get(SaveAsTemplateAction.class),
null,
SystemAction.get(FileSystemAction.class),
null,
SystemAction.get(ToolsAction.class),
SystemAction.get(PropertiesAction.class)
};
}
@Override
public Action getPreferredAction() {
return getActions(false)[0];
}
/** Gets the name. Note: It gets only the local part of the name (e.g. "de_DE_EURO").
* Reason is to allow user change only this part of name by renaming (on Node).
* Overrides superclass method.
*
* @return locale part of name
*/
@Override
public String getName() {
String localeName = "invalid"; // NOI18N
if (getFileEntry().getFile().isValid() && !getFileEntry().getFile().isVirtual()) {
localeName = Util.getLocaleSuffix (getFileEntry());
if (localeName.length() > 0) {
if (localeName.charAt(0) == PropertiesDataLoader.PRB_SEPARATOR_CHAR) {
localeName = localeName.substring(1);
}
}
}
return localeName;
}
/** Sets the system name. Overrides superclass method.
*
* @param name the new name
*/
@Override
public void setName (String name) {
if(!name.startsWith(getFileEntry().getDataObject().getPrimaryFile().getName())) {
name = Util.assembleName (getFileEntry().getDataObject().getPrimaryFile().getName(), name);
}
// new name is same as old one, do nothing
if (name.equals(super.getName())) {
return;
}
super.setName (name);
setDisplayName(Util.getLocaleLabel(getFileEntry()));
setShortDescription(messageToolTip());
}
/** Gets tooltip message for this node. Helper method. */
private String messageToolTip () {
FileObject fo = getFileEntry().getFile();
return FileUtil.getFileDisplayName(fo);
}
@Override
public String getHtmlDisplayName() { // inspired by DataNode
try {
StatusDecorator stat = getFileEntry().getFile().getFileSystem().getDecorator();
String result = stat.annotateNameHtml (
super.getDisplayName(), Collections.singleton(getFileEntry().getFile()));
//Make sure the super string was really modified
if (result != null && !super.getDisplayName().equals(result)) {
return result;
}
} catch (FileStateInvalidException e) {
//do nothing and fall through
}
return super.getHtmlDisplayName();
}
/** This node can be renamed. Overrides superclass method. */
@Override
public boolean canRename() {
return getFileEntry().isDeleteAllowed ();
}
/** List new types that can be created in this node. Overrides superclass method.
* @return new types
*/
@Override
public NewType[] getNewTypes () {
return new NewType[] { new NewPropertyType((PropertiesFileEntry)getFileEntry()) };
}
static class NewPropertyType extends NewType {
private PropertiesFileEntry pfEntry;
NewPropertyType(PropertiesFileEntry pfe) {
pfEntry = pfe;
}
/** Getter for name property. */
@Override
public String getName() {
return NbBundle.getBundle(PropertiesLocaleNode.class).getString("LAB_NewPropertyAction");
}
/** Gets help context. */
@Override
public HelpCtx getHelpCtx() {
return new HelpCtx(Util.HELP_ID_ADDING);
}
/** Creates new type. */
public void create() throws IOException {
final PropertyPanel panel = new PropertyPanel();
Object selectedOption = DialogDisplayer.getDefault().notify(
new DialogDescriptor(
panel,
NbBundle.getMessage(BundleEditPanel.class,
"CTL_NewPropertyTitle"))); //NOI18N
if (selectedOption != NotifyDescriptor.OK_OPTION) {
return;
}
String key = panel.getKey();
String value = panel.getValue();
String comment = panel.getComment();
// add key to all entries
if(!pfEntry.getHandler().getStructure().addItem(key, value, comment)) {
DialogDisplayer.getDefault().notify(
new NotifyDescriptor.Message(
NbBundle.getMessage(
PropertiesLocaleNode.class, "MSG_KeyExists",
key,
Util.getLocaleLabel(pfEntry)),
NotifyDescriptor.ERROR_MESSAGE));
}
}
}
/** Indicates if this node has a customizer. Overrides superclass method.
* @return true */
@Override
public boolean hasCustomizer() {
return true;
}
/** Gets node customizer. Overrides superclass method. */
@Override
public Component getCustomizer() {
return new LocaleNodeCustomizer((PropertiesFileEntry)getFileEntry());
}
/** Creates paste types for this node. Overrides superclass method. */
@Override
protected void createPasteTypes(Transferable t, List<PasteType> s) {
super.createPasteTypes(t, s);
Element.ItemElem item;
Node n = NodeTransfer.node(t, NodeTransfer.MOVE);
// cut
if (n != null && n.canDestroy ()) {
item = n.getCookie(Element.ItemElem.class);
if (item != null) {
// are we pasting into the same node
Node n2 = getChildren().findChild(item.getKey());
if (n == n2) {
return;
}
s.add(new KeyPasteType(item, n, KeyPasteType.MODE_PASTE_WITH_VALUE));
s.add(new KeyPasteType(item, n, KeyPasteType.MODE_PASTE_WITHOUT_VALUE));
return;
}
}
// copy
else {
item = NodeTransfer.cookie(t, NodeTransfer.COPY, Element.ItemElem.class);
if (item != null) {
s.add(new KeyPasteType(item, null, KeyPasteType.MODE_PASTE_WITH_VALUE));
s.add(new KeyPasteType(item, null, KeyPasteType.MODE_PASTE_WITHOUT_VALUE));
return;
}
}
}
/** Paste type for keys. */
private class KeyPasteType extends PasteType {
/** Transferred item. */
private Element.ItemElem item;
/** The node to destroy or null. */
private Node node;
/** Paste mode. */
int mode;
/** Paste with value mode. */
public static final int MODE_PASTE_WITH_VALUE = 1;
/** Paste without value mode. */
public static final int MODE_PASTE_WITHOUT_VALUE = 2;
/** Constructs new <code>KeyPasteType</code> for the specific type of operation paste. */
public KeyPasteType(Element.ItemElem item, Node node, int mode) {
this.item = item;
this.node = node;
this.mode = mode;
}
/** Gets name.
* @return human presentable name of this paste type. */
@Override
public String getName() {
String pasteKey = mode == 1 ? "CTL_PasteKeyValue" : "CTL_PasteKeyNoValue";
return NbBundle.getBundle(PropertiesLocaleNode.class).getString(pasteKey);
}
/** Performs the paste action.
* @return <code>Transferable</code> which should be inserted into the clipboard after
* paste action. It can be null, which means that clipboard content
* should stay the same
*/
public Transferable paste() throws IOException {
PropertiesStructure ps = ((PropertiesFileEntry)getFileEntry()).getHandler().getStructure();
String value;
if (mode == MODE_PASTE_WITH_VALUE) {
value = item.getValue();
} else {
value = "";
}
if (ps != null) {
Element.ItemElem newItem = ps.getItem(item.getKey());
if (newItem == null) {
ps.addItem(item.getKey(), value, item.getComment());
}
else {
newItem.setValue(value);
newItem.setComment(item.getComment());
}
if (node != null) {
node.destroy();
}
}
return null;
}
} // End of inner KeyPasteType class.
}