| /* |
| * 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. |
| |
| } |