001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.project.ui.actions;
043:
044: import java.awt.event.ActionEvent;
045: import java.awt.event.ActionListener;
046: import java.beans.BeanInfo;
047: import java.beans.PropertyChangeEvent;
048: import java.beans.PropertyChangeListener;
049: import java.io.IOException;
050: import java.text.MessageFormat;
051: import java.util.ArrayList;
052: import java.util.Iterator;
053: import java.util.List;
054: import java.util.Set;
055: import javax.swing.*;
056: import javax.swing.JPopupMenu.Separator;
057: import javax.swing.event.PopupMenuEvent;
058: import javax.swing.event.PopupMenuListener;
059: import org.netbeans.api.project.Project;
060: import org.netbeans.modules.project.ui.NewFileWizard;
061: import org.netbeans.modules.project.ui.NoProjectNew;
062: import org.netbeans.modules.project.ui.OpenProjectList;
063: import org.netbeans.modules.project.ui.ProjectUtilities;
064: import org.netbeans.spi.project.ui.templates.support.Templates;
065: import org.openide.ErrorManager;
066: import org.openide.awt.DynamicMenuContent;
067: import org.openide.awt.Mnemonics;
068: import org.openide.filesystems.FileObject;
069: import org.openide.loaders.DataFolder;
070: import org.openide.loaders.DataObject;
071: import org.openide.loaders.DataObjectNotFoundException;
072: import org.openide.nodes.Node;
073: import org.openide.util.Lookup;
074: import org.openide.util.Mutex;
075: import org.openide.util.NbBundle;
076: import org.openide.util.Utilities;
077: import org.openide.util.WeakListeners;
078: import org.openide.util.actions.Presenter.Popup;
079:
080: /** Action for invoking the project sensitive NewFile Wizard
081: */
082: public class NewFile extends ProjectAction implements
083: PropertyChangeListener, Popup {
084:
085: private static final Icon ICON = new ImageIcon(
086: Utilities
087: .loadImage("org/netbeans/modules/project/ui/resources/newFile.png")); //NOI18N
088: private static final String _NAME = NbBundle.getMessage(
089: NewFile.class, "LBL_NewFileAction_Name");
090: private static final String _SHORT_DESCRIPTION = NbBundle
091: .getMessage(NewFile.class, "LBL_NewFileAction_Tooltip");
092: private static final String POPUP_NAME = NbBundle.getMessage(
093: NewFile.class, "LBL_NewFileAction_PopupName");
094: private static final String FILE_POPUP_NAME = NbBundle.getMessage(
095: NewFile.class, "LBL_NewFileAction_File_PopupName");
096: private static final String TEMPLATE_NAME_FORMAT = NbBundle
097: .getMessage(NewFile.class,
098: "LBL_NewFileAction_Template_PopupName");
099:
100: private JMenu subMenu;
101:
102: public NewFile() {
103: this (null);
104: }
105:
106: public NewFile(Lookup context) {
107: super ((String) null, _NAME, ICON, context); //NOI18N
108: putValue("iconBase",
109: "org/netbeans/modules/project/ui/resources/newFile.png"); //NOI18N
110: putValue(SHORT_DESCRIPTION, _SHORT_DESCRIPTION);
111: OpenProjectList.getDefault().addPropertyChangeListener(
112: WeakListeners.propertyChange(this , OpenProjectList
113: .getDefault()));
114: refresh(getLookup());
115: }
116:
117: @Override
118: protected void refresh(Lookup context) {
119: // #59615: update synch if possible; only replan if not already in EQ.
120: Mutex.EVENT.readAccess(new Runnable() {
121: public void run() {
122: setEnabled(OpenProjectList.getDefault()
123: .getOpenProjects().length > 0);
124: setDisplayName(_NAME);
125: }
126: });
127: }
128:
129: public JMenuItem getPopupPresenter() {
130: JMenuItem menu = new JMenuItem(this );
131: menu.setIcon(null);
132: Mnemonics
133: .setLocalizedText(menu, (String) getValue(Action.NAME));
134: // XXX accelerator not displayed here for some reason...why???
135: return menu;
136: }
137:
138: //private NewFileWizard wizardIterator;
139:
140: @Override
141: protected void actionPerformed(Lookup context) {
142: doPerform(context, null, true);
143: }
144:
145: private void doPerform(Lookup context, DataObject template,
146: boolean inProject) {
147:
148: if (context == null) {
149: context = getLookup();
150: }
151:
152: if (!inProject) {
153: // Context outside of projects
154: NoProjectNew.showDialog(template,
155: preselectedFolder(context));
156: return;
157: }
158:
159: NewFileWizard wd = new NewFileWizard(
160: preselectedProject(context) /* , null */);
161:
162: DataFolder preselectedFolder = preselectedFolder(context);
163: if (preselectedFolder != null) {
164: wd.setTargetFolder(preselectedFolder);
165: }
166:
167: try {
168: Set resultSet = template == null ? wd.instantiate() : wd
169: .instantiate(template);
170:
171: if (resultSet == null || resultSet.isEmpty()) {
172: // no new object, no work
173: return;
174: }
175:
176: Iterator it = resultSet.iterator();
177:
178: while (it.hasNext()) {
179: Object obj = it.next();
180: DataObject newDO = null;
181: if (obj instanceof DataObject) {
182: newDO = (DataObject) obj;
183: } else if (obj instanceof FileObject) {
184: try {
185: newDO = DataObject.find((FileObject) obj);
186: } catch (DataObjectNotFoundException x) {
187: // XXX
188: assert false : obj;
189: }
190: } else {
191: assert false : obj;
192: }
193: if (newDO != null) {
194: ProjectUtilities.openAndSelectNewObject(newDO);
195: }
196: }
197: } catch (IOException e) {
198: ErrorManager.getDefault().notify(
199: ErrorManager.INFORMATIONAL, e);
200: }
201:
202: // Update the Templates LRU for given project
203: //Project project = Templates.getProject( wd );
204: FileObject foTemplate = Templates.getTemplate(wd);
205: OpenProjectList.getDefault().updateTemplatesLRU(foTemplate);
206:
207: }
208:
209: // Context Aware action implementation -------------------------------------
210:
211: @Override
212: public Action createContextAwareInstance(Lookup actionContext) {
213: return new NewFile(actionContext);
214: }
215:
216: // Presenter.Popup implementation ------------------------------------------
217:
218: public JMenuItem getSubmenuPopupPresenter() {
219: if (subMenu == null) {
220: subMenu = new JMenu(POPUP_NAME);
221: }
222: return subMenu;
223: }
224:
225: protected void fillSubMenu() {
226: Project projects[] = ActionsUtil.getProjectsFromLookup(
227: getLookup(), null);
228: if (projects != null && projects.length > 0) {
229: fillSubMenu(subMenu, projects[0]);
230: } else {
231: // When no project is seleceted only file and folder can be created
232: fillNonProjectSubMenu(subMenu);
233: }
234: }
235:
236: // Private methods ---------------------------------------------------------
237:
238: private Project preselectedProject(Lookup context) {
239: Project preselectedProject = null;
240:
241: // if ( activatedNodes != null && activatedNodes.length != 0 ) {
242:
243: Project[] projects = ActionsUtil.getProjectsFromLookup(context,
244: null);
245: if (projects.length > 0) {
246: preselectedProject = projects[0];
247: }
248:
249: if (preselectedProject == null) {
250: // No project context => use main project
251: preselectedProject = OpenProjectList.getDefault()
252: .getMainProject();
253: if (preselectedProject == null) {
254: // No main project => use the first one
255: preselectedProject = OpenProjectList.getDefault()
256: .getOpenProjects()[0];
257: }
258: }
259:
260: if (preselectedProject == null) {
261: assert false : "Action should be disabled"; // NOI18N
262: }
263:
264: return preselectedProject;
265: }
266:
267: private DataFolder preselectedFolder(Lookup context) {
268:
269: DataFolder preselectedFolder = null;
270:
271: // Try to find selected folder
272: preselectedFolder = context.lookup(DataFolder.class);
273: if (preselectedFolder == null) {
274: // No folder selectd try with DataObject
275: DataObject dobj = context.lookup(DataObject.class);
276: if (dobj != null) {
277: // DataObject found => we'll use the parent folder
278: preselectedFolder = dobj.getFolder();
279: }
280: }
281:
282: return preselectedFolder;
283: }
284:
285: public void propertyChange(PropertyChangeEvent evt) {
286: refresh(Lookup.EMPTY);
287: }
288:
289: public static String TEMPLATE_PROPERTY = "org.netbeans.modules.project.ui.actions.NewFile.Template"; // NOI18N
290: public static String IN_PROJECT_PROPERTY = "org.netbeans.modules.project.ui.actions.NewFile.InProject"; // NOI18N
291:
292: private void fillSubMenu(JMenu menuItem, Project project) {
293: menuItem.removeAll();
294:
295: ActionListener menuListener = new PopupListener();
296:
297: List lruList = OpenProjectList.getDefault().getTemplatesLRU(
298: project);
299: boolean itemAdded = false;
300: for (Iterator it = lruList.iterator(); it.hasNext();) {
301: DataObject template = (DataObject) it.next();
302:
303: Node delegate = template.getNodeDelegate();
304: JMenuItem item = new JMenuItem(MessageFormat.format(
305: TEMPLATE_NAME_FORMAT, new Object[] { delegate
306: .getDisplayName() }), new ImageIcon(
307: delegate.getIcon(BeanInfo.ICON_COLOR_16x16)));
308: item.addActionListener(menuListener);
309: item.putClientProperty(TEMPLATE_PROPERTY, template);
310: menuItem.add(item);
311: itemAdded = true;
312: }
313: if (itemAdded) {
314: menuItem.add(new Separator());
315: }
316: JMenuItem fileItem = new JMenuItem(FILE_POPUP_NAME,
317: (Icon) getValue(Action.SMALL_ICON));
318: fileItem.addActionListener(menuListener);
319: fileItem.putClientProperty(TEMPLATE_PROPERTY, null);
320: menuItem.add(fileItem);
321: }
322:
323: private void fillNonProjectSubMenu(JMenu menuItem) {
324: menuItem.removeAll();
325:
326: ActionListener menuListener = new PopupListener();
327:
328: DataFolder preselectedFolder = preselectedFolder(getLookup());
329:
330: boolean canWrite;
331: if (preselectedFolder == null) {
332: canWrite = false;
333: } else {
334: FileObject pf = preselectedFolder.getPrimaryFile();
335: canWrite = pf != null && pf.canWrite();
336: }
337:
338: DataObject templates[] = NoProjectNew.getTemplates();
339: for (int i = 0; i < templates.length; i++) {
340: Node n = templates[i].getNodeDelegate();
341: JMenuItem item = new JMenuItem(MessageFormat.format(
342: TEMPLATE_NAME_FORMAT, new Object[] { n
343: .getDisplayName() }), new ImageIcon(n
344: .getIcon(BeanInfo.ICON_COLOR_16x16)));
345: item.addActionListener(menuListener);
346: item.putClientProperty(TEMPLATE_PROPERTY, templates[i]);
347: item.putClientProperty(IN_PROJECT_PROPERTY, Boolean.FALSE);
348: item.setEnabled(canWrite);
349: menuItem.add(item);
350: }
351: }
352:
353: private class PopupListener implements ActionListener {
354:
355: public void actionPerformed(ActionEvent e) {
356: JMenuItem source = (JMenuItem) e.getSource();
357:
358: Boolean inProject = (Boolean) source
359: .getClientProperty(IN_PROJECT_PROPERTY);
360: DataObject template = (DataObject) source
361: .getClientProperty(TEMPLATE_PROPERTY);
362:
363: if (inProject != null && inProject == Boolean.FALSE) {
364: doPerform(null, template, false);
365: } else {
366: doPerform(null, template, true);
367: }
368: }
369:
370: }
371:
372: /**
373: * Variant for folder context menus that makes a submenu.
374: */
375: public static final class WithSubMenu extends NewFile {
376:
377: public WithSubMenu() {
378: }
379:
380: private WithSubMenu(Lookup actionContext) {
381: super (actionContext);
382: }
383:
384: @Override
385: public JMenuItem getPopupPresenter() {
386: return new DynaMenu(POPUP_NAME);
387: }
388:
389: @Override
390: public Action createContextAwareInstance(Lookup actionContext) {
391: return new WithSubMenu(actionContext);
392: }
393:
394: private final class DynaMenu extends JMenu implements
395: DynamicMenuContent {
396:
397: public DynaMenu(String a) {
398: super (a);
399: }
400:
401: public JComponent[] getMenuPresenters() {
402: JComponent jc = getSubmenuPopupPresenter();
403: fillSubMenu();
404: return new JComponent[] { jc };
405: }
406:
407: public JComponent[] synchMenuPresenters(JComponent[] items) {
408: JComponent jc = getSubmenuPopupPresenter();
409: fillSubMenu();
410: return new JComponent[] { jc };
411: }
412: }
413: }
414:
415: }
|