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.uiapi;
043:
044: import java.awt.Component;
045: import java.awt.Dialog;
046: import java.awt.Window;
047: import java.awt.event.ActionEvent;
048: import java.awt.event.ActionListener;
049: import java.awt.event.KeyEvent;
050: import java.io.File;
051: import java.io.IOException;
052: import java.util.ArrayList;
053: import java.util.Arrays;
054: import java.util.Collection;
055: import java.util.Iterator;
056: import java.util.List;
057: import javax.swing.Action;
058: import javax.swing.JButton;
059: import javax.swing.JComponent;
060: import javax.swing.JDialog;
061: import javax.swing.KeyStroke;
062: import javax.swing.SwingUtilities;
063: import javax.swing.border.EmptyBorder;
064: import javax.swing.event.ChangeEvent;
065: import javax.swing.event.ChangeListener;
066: import org.netbeans.api.progress.ProgressHandle;
067: import org.netbeans.api.progress.ProgressHandleFactory;
068: import org.netbeans.api.project.FileOwnerQuery;
069: import org.netbeans.api.project.Project;
070: import org.netbeans.api.project.ProjectManager;
071: import org.netbeans.api.project.ProjectUtils;
072: import org.netbeans.api.project.ui.OpenProjects;
073: import org.netbeans.api.queries.SharabilityQuery;
074: import org.netbeans.spi.project.MoveOperationImplementation;
075: import org.netbeans.spi.project.support.ProjectOperations;
076: import org.netbeans.api.queries.VisibilityQuery;
077: import org.netbeans.spi.project.ui.support.CommonProjectActions;
078: import org.openide.DialogDescriptor;
079: import org.openide.DialogDisplayer;
080: import org.openide.ErrorManager;
081: import org.openide.LifecycleManager;
082: import org.openide.NotifyDescriptor;
083: import org.openide.awt.Mnemonics;
084: import org.openide.filesystems.FileObject;
085: import org.openide.filesystems.FileUtil;
086: import org.openide.loaders.DataFolder;
087: import org.openide.loaders.DataObject;
088: import org.openide.loaders.DataObjectNotFoundException;
089: import org.openide.util.ContextAwareAction;
090: import org.openide.util.Mutex;
091: import org.openide.util.NbBundle;
092: import org.openide.util.RequestProcessor;
093: import org.openide.util.lookup.Lookups;
094:
095: /**
096: * @author Jan Lahoda
097: */
098: public final class DefaultProjectOperationsImplementation {
099:
100: private static final ErrorManager ERR = ErrorManager.getDefault(); // NOI18N
101:
102: //fractions how many time will be spent in some phases of the move and copy operation
103: //the rename and delete operation use a different approach:
104: private static final double NOTIFY_WORK = 0.1;
105: private static final double FIND_PROJECT_WORK = 0.1;
106: static final int MAX_WORK = 100;
107:
108: private DefaultProjectOperationsImplementation() {
109: }
110:
111: private static String getDisplayName(Project project) {
112: return ProjectUtils.getInformation(project).getDisplayName();
113: }
114:
115: //<editor-fold defaultstate="collapsed" desc="Delete Operation">
116: /**
117: * @return true if success
118: */
119: private static boolean performDelete(Project project,
120: List<FileObject> toDelete, ProgressHandle handle)
121: throws Exception {
122: try {
123: handle.start(toDelete.size() + 1 /*clean*/);
124:
125: int done = 0;
126:
127: handle.progress(NbBundle.getMessage(
128: DefaultProjectOperationsImplementation.class,
129: "LBL_Progress_Cleaning_Project"));
130:
131: ProjectOperations.notifyDeleting(project);
132:
133: handle.progress(++done);
134:
135: for (FileObject f : toDelete) {
136: handle.progress(NbBundle.getMessage(
137: DefaultProjectOperationsImplementation.class,
138: "LBL_Progress_Deleting_File", FileUtil
139: .getFileDisplayName(f)));
140:
141: if (f != null)
142: f.delete();
143:
144: handle.progress(++done);
145: }
146:
147: FileObject projectFolder = project.getProjectDirectory();
148:
149: if (projectFolder.getChildren().length == 0) {
150: //empty, delete:
151: projectFolder.delete();
152: }
153:
154: handle.finish();
155:
156: ProjectOperations.notifyDeleted(project);
157: return true;
158: } catch (Exception e) {
159: String displayName = getDisplayName(project);
160: String message = NbBundle.getMessage(
161: DefaultProjectOperationsImplementation.class,
162: "LBL_Project_cannot_be_deleted.", displayName);
163:
164: ErrorManager.getDefault().annotate(e, message);
165:
166: return false;
167: }
168: }
169:
170: public static void deleteProject(final Project project) {
171: deleteProject(project, new GUIUserInputHandler());
172: }
173:
174: static void deleteProject(final Project project,
175: UserInputHandler handler) {
176: String displayName = getDisplayName(project);
177: FileObject projectFolder = project.getProjectDirectory();
178:
179: if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) {
180: ERR.log(ErrorManager.INFORMATIONAL, "delete started: "
181: + displayName); // NOI18N
182: }
183:
184: final List<FileObject> metadataFiles = ProjectOperations
185: .getMetadataFiles(project);
186: final List<FileObject> dataFiles = ProjectOperations
187: .getDataFiles(project);
188: final List<FileObject> allFiles = new ArrayList<FileObject>();
189:
190: allFiles.addAll(metadataFiles);
191: allFiles.addAll(dataFiles);
192:
193: for (Iterator<FileObject> i = allFiles.iterator(); i.hasNext();) {
194: FileObject f = i.next();
195: if (!FileUtil.isParentOf(projectFolder, f)) {
196: i.remove();
197: }
198: }
199:
200: final ProgressHandle handle = ProgressHandleFactory
201: .createHandle(NbBundle.getMessage(
202: DefaultProjectOperationsImplementation.class,
203: "LBL_Delete_Project_Caption"));
204: final DefaultProjectDeletePanel deletePanel = new DefaultProjectDeletePanel(
205: handle, displayName, FileUtil
206: .getFileDisplayName(projectFolder), !dataFiles
207: .isEmpty());
208:
209: String caption = NbBundle.getMessage(
210: DefaultProjectOperationsImplementation.class,
211: "LBL_Delete_Project_Caption");
212:
213: handler.showConfirmationDialog(deletePanel, project, caption,
214: "Yes_Button", "No_Button", true, new Executor() { // NOI18N
215: public void execute() throws Exception {
216: close(project);
217:
218: if (deletePanel.isDeleteSources()) {
219: performDelete(project, allFiles, handle);
220: } else {
221: performDelete(project, metadataFiles,
222: handle);
223: }
224: }
225: });
226:
227: if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) {
228: ERR.log(ErrorManager.INFORMATIONAL, "delete done: "
229: + displayName); // NOI18N
230: }
231: }
232:
233: static interface UserInputHandler {
234: void showConfirmationDialog(final JComponent panel,
235: Project project, String caption, String confirmButton,
236: String cancelButton, boolean doSetMessageType,
237: final Executor executor);
238: }
239:
240: private static final class GUIUserInputHandler implements
241: UserInputHandler {
242:
243: public void showConfirmationDialog(final JComponent panel,
244: Project project, String caption, String confirmButton,
245: String cancelButton, boolean doSetMessageType,
246: final Executor executor) {
247: DefaultProjectOperationsImplementation
248: .showConfirmationDialog(panel, project, caption,
249: confirmButton, cancelButton,
250: doSetMessageType, executor);
251: }
252:
253: }
254:
255: //</editor-fold>
256:
257: //<editor-fold defaultstate="collapsed" desc="Copy Operation">
258: public static void copyProject(final Project project) {
259: final ProgressHandle handle = ProgressHandleFactory
260: .createHandle(NbBundle.getMessage(
261: DefaultProjectOperationsImplementation.class,
262: "LBL_Copy_Project_Handle"));
263: final ProjectCopyPanel panel = new ProjectCopyPanel(handle,
264: project, false);
265: //#76559
266: handle.start(MAX_WORK);
267:
268: showConfirmationDialog(panel, project, NbBundle.getMessage(
269: DefaultProjectOperationsImplementation.class,
270: "LBL_Copy_Project_Caption"), "Copy_Button", null,
271: false, new Executor() { // NOI18N
272: public void execute() throws Exception {
273: String nueName = panel.getNewName();
274: File newTarget = FileUtil.normalizeFile(panel
275: .getNewDirectory());
276:
277: FileObject newTargetFO = FileUtil
278: .toFileObject(newTarget);
279: if (newTargetFO == null) {
280: newTargetFO = createFolder(newTarget
281: .getParentFile(), newTarget
282: .getName());
283: }
284: doCopyProject(handle, project, nueName,
285: newTargetFO);
286: }
287: });
288: }
289:
290: /*package private for tests*/static void doCopyProject(
291: ProgressHandle handle, Project project, String nueName,
292: FileObject newTarget) throws Exception {
293: try {
294: int totalWork = MAX_WORK;
295:
296: double currentWorkDone = 0;
297:
298: handle.progress((int) currentWorkDone);
299:
300: ProjectOperations.notifyCopying(project);
301:
302: handle.progress((int) (currentWorkDone = totalWork
303: * NOTIFY_WORK));
304:
305: FileObject target = newTarget.createFolder(nueName);
306: FileObject projectDirectory = project.getProjectDirectory();
307: List<FileObject> toCopyList = new ArrayList<FileObject>();
308: for (FileObject child : projectDirectory.getChildren()) {
309: if (child.isValid()) {
310: toCopyList.add(child);
311: }
312: }
313:
314: double workPerFileAndOperation = totalWork
315: * (1.0 - 2 * NOTIFY_WORK - FIND_PROJECT_WORK)
316: / toCopyList.size();
317:
318: for (FileObject toCopy : toCopyList) {
319: doCopy(project, toCopy, target);
320:
321: int lastWorkDone = (int) currentWorkDone;
322:
323: currentWorkDone += workPerFileAndOperation;
324:
325: if (lastWorkDone < (int) currentWorkDone) {
326: handle.progress((int) currentWorkDone);
327: }
328: }
329:
330: //#64264: the non-project cache can be filled with incorrect data (gathered during the project copy phase), clear it:
331: ProjectManager.getDefault().clearNonProjectCache();
332: Project nue = ProjectManager.getDefault().findProject(
333: target);
334:
335: assert nue != null;
336:
337: handle.progress((int) (currentWorkDone += totalWork
338: * FIND_PROJECT_WORK));
339:
340: ProjectOperations.notifyCopied(project, nue, FileUtil
341: .toFile(project.getProjectDirectory()), nueName);
342:
343: handle.progress((int) (currentWorkDone += totalWork
344: * NOTIFY_WORK));
345:
346: ProjectManager.getDefault().saveProject(nue);
347:
348: open(nue, false);
349:
350: handle.progress(totalWork);
351: handle.finish();
352: } catch (Exception e) {
353: ErrorManager
354: .getDefault()
355: .annotate(
356: e,
357: NbBundle
358: .getMessage(
359: DefaultProjectOperationsImplementation.class,
360: "ERR_Cannot_Move",
361: e.getLocalizedMessage()));
362: throw e;
363: }
364: }
365:
366: //</editor-fold>
367:
368: //<editor-fold defaultstate="collapsed" desc="Move Operation">
369: public static void moveProject(final Project project) {
370: final ProgressHandle handle = ProgressHandleFactory
371: .createHandle(NbBundle.getMessage(
372: DefaultProjectOperationsImplementation.class,
373: "LBL_Move_Project_Handle"));
374: final ProjectCopyPanel panel = new ProjectCopyPanel(handle,
375: project, true);
376: //#76559
377: handle.start(MAX_WORK);
378:
379: showConfirmationDialog(panel, project, NbBundle.getMessage(
380: DefaultProjectOperationsImplementation.class,
381: "LBL_Move_Project_Caption"), "Move_Button", null,
382: false, new Executor() { // NOI18N
383: public void execute() throws Exception {
384: String nueFolderName = panel
385: .getProjectFolderName();
386: String nueProjectName = panel.getNewName();
387: File newTarget = FileUtil.normalizeFile(panel
388: .getNewDirectory());
389:
390: FileObject newTargetFO = FileUtil
391: .toFileObject(newTarget);
392: if (newTargetFO == null) {
393: newTargetFO = createFolder(newTarget
394: .getParentFile(), newTarget
395: .getName());
396: }
397:
398: doMoveProject(handle, project, nueFolderName,
399: nueProjectName, newTargetFO,
400: "ERR_Cannot_Move");
401: }
402: });
403: }
404:
405: public static void renameProject(Project project) {
406: renameProject(project, null);
407: }
408:
409: public static void renameProject(final Project project,
410: final String nueName) {
411: final ProgressHandle handle = ProgressHandleFactory
412: .createHandle(NbBundle.getMessage(
413: DefaultProjectOperationsImplementation.class,
414: "LBL_Rename_Project_Handle"));
415: final DefaultProjectRenamePanel panel = new DefaultProjectRenamePanel(
416: handle, project, nueName);
417:
418: //#76559
419: handle.start(MAX_WORK);
420:
421: showConfirmationDialog(panel, project, NbBundle.getMessage(
422: DefaultProjectOperationsImplementation.class,
423: "LBL_Rename_Project_Caption"), "Rename_Button", null,
424: false, new Executor() { // NOI18N
425: public void execute() throws Exception {
426: String nueName = panel.getNewName();
427:
428: if (panel.getRenameProjectFolder()) {
429:
430: doMoveProject(handle, project, nueName,
431: nueName, project
432: .getProjectDirectory()
433: .getParent(),
434: "ERR_Cannot_Rename");
435: } else {
436: boolean originalOK = true;
437: Project main = OpenProjects.getDefault()
438: .getMainProject();
439: boolean wasMain = main != null
440: && project
441: .getProjectDirectory()
442: .equals(
443: main
444: .getProjectDirectory());
445: Project nue = null;
446:
447: try {
448: handle.switchToIndeterminate();
449: handle.switchToDeterminate(5);
450:
451: int currentWorkDone = 0;
452:
453: FileObject projectDirectory = project
454: .getProjectDirectory();
455: File projectDirectoryFile = FileUtil
456: .toFile(project
457: .getProjectDirectory());
458: Collection<? extends MoveOperationImplementation> operations = project
459: .getLookup()
460: .lookupAll(
461: MoveOperationImplementation.class);
462:
463: close(project);
464:
465: handle.progress(++currentWorkDone);
466:
467: for (MoveOperationImplementation o : operations) {
468: o.notifyMoving();
469: }
470:
471: handle.progress(++currentWorkDone);
472:
473: for (MoveOperationImplementation o : operations) {
474: o.notifyMoved(null,
475: projectDirectoryFile,
476: nueName);
477: }
478:
479: handle.progress(++currentWorkDone);
480:
481: //#64264: the non-project cache can be filled with incorrect data (gathered during the project copy phase), clear it:
482: ProjectManager.getDefault()
483: .clearNonProjectCache();
484:
485: nue = ProjectManager.getDefault()
486: .findProject(projectDirectory);
487:
488: assert nue != null;
489:
490: originalOK = false;
491:
492: handle.progress(++currentWorkDone);
493:
494: operations = nue
495: .getLookup()
496: .lookupAll(
497: MoveOperationImplementation.class);
498:
499: for (MoveOperationImplementation o : operations) {
500: o.notifyMoved(project,
501: projectDirectoryFile,
502: nueName);
503: }
504:
505: ProjectManager.getDefault()
506: .saveProject(nue);
507:
508: open(nue, wasMain);
509:
510: handle.progress(++currentWorkDone);
511:
512: handle.finish();
513: } catch (Exception e) {
514: if (originalOK) {
515: open(project, wasMain);
516: } else {
517: assert nue != null;
518: open(nue, wasMain);
519: }
520: ErrorManager
521: .getDefault()
522: .annotate(
523: e,
524: NbBundle
525: .getMessage(
526: DefaultProjectOperationsImplementation.class,
527: "ERR_Cannot_Rename",
528: e
529: .getLocalizedMessage()));
530: throw e;
531: }
532: }
533: }
534: });
535: }
536:
537: /*package private for tests*/static void doMoveProject(
538: ProgressHandle handle, Project project,
539: String nueFolderName, String nueProjectName,
540: FileObject newTarget, String errorKey) throws Exception {
541: boolean originalOK = true;
542: Project main = OpenProjects.getDefault().getMainProject();
543: boolean wasMain = main != null
544: && project.getProjectDirectory().equals(
545: main.getProjectDirectory());
546: FileObject target = null;
547:
548: try {
549:
550: int totalWork = MAX_WORK;
551: double currentWorkDone = 0;
552:
553: handle.progress((int) currentWorkDone);
554:
555: ProjectOperations.notifyMoving(project);
556:
557: close(project);
558:
559: handle.progress((int) (currentWorkDone = totalWork
560: * NOTIFY_WORK));
561:
562: FileObject projectDirectory = project.getProjectDirectory();
563: List<FileObject> toMoveList = new ArrayList<FileObject>();
564: for (FileObject child : projectDirectory.getChildren()) {
565: if (child.isValid()) {
566: toMoveList.add(child);
567: }
568: }
569:
570: double workPerFileAndOperation = (totalWork
571: * (1.0 - 2 * NOTIFY_WORK - FIND_PROJECT_WORK) / toMoveList
572: .size()) / 2;
573:
574: target = newTarget.createFolder(nueFolderName);
575:
576: for (FileObject toCopy : toMoveList) {
577: doCopy(project, toCopy, target);
578:
579: int lastWorkDone = (int) currentWorkDone;
580:
581: currentWorkDone += workPerFileAndOperation;
582:
583: if (lastWorkDone < (int) currentWorkDone) {
584: handle.progress((int) currentWorkDone);
585: }
586: }
587:
588: originalOK = false;
589:
590: for (FileObject toCopy : toMoveList) {
591: doDelete(project, toCopy);
592:
593: int lastWorkDone = (int) currentWorkDone;
594:
595: currentWorkDone += workPerFileAndOperation;
596:
597: if (lastWorkDone < (int) currentWorkDone) {
598: handle.progress((int) currentWorkDone);
599: }
600: }
601:
602: if (projectDirectory.getChildren().length == 0) {
603: projectDirectory.delete();
604: }
605:
606: //#64264: the non-project cache can be filled with incorrect data (gathered during the project copy phase), clear it:
607: ProjectManager.getDefault().clearNonProjectCache();
608: Project nue = ProjectManager.getDefault().findProject(
609: target);
610:
611: handle.progress((int) (currentWorkDone += totalWork
612: * FIND_PROJECT_WORK));
613:
614: assert nue != null;
615:
616: ProjectOperations.notifyMoved(project, nue, FileUtil
617: .toFile(project.getProjectDirectory()),
618: nueProjectName);
619:
620: handle.progress((int) (currentWorkDone += totalWork
621: * NOTIFY_WORK));
622:
623: ProjectManager.getDefault().saveProject(nue);
624:
625: open(nue, wasMain);
626:
627: handle.progress(totalWork);
628: handle.finish();
629: } catch (Exception e) {
630: if (originalOK) {
631: open(project, wasMain);
632: } else {
633: assert target != null;
634:
635: //#64264: the non-project cache can be filled with incorrect data (gathered during the project copy phase), clear it:
636: ProjectManager.getDefault().clearNonProjectCache();
637: Project nue = ProjectManager.getDefault().findProject(
638: target);
639:
640: assert nue != null;
641:
642: open(nue, wasMain);
643: }
644: ErrorManager
645: .getDefault()
646: .annotate(
647: e,
648: NbBundle
649: .getMessage(
650: DefaultProjectOperationsImplementation.class,
651: errorKey,
652: e.getLocalizedMessage()));
653: throw e;
654: }
655: }
656:
657: //</editor-fold>
658:
659: //<editor-fold defaultstate="collapsed" desc="Copy Move Utilities">
660: private static void doCopy(Project original, FileObject from,
661: FileObject toParent) throws IOException {
662: if (!VisibilityQuery.getDefault().isVisible(from)) {
663: //Do not copy invisible files/folders.
664: return;
665: }
666:
667: if (!original.getProjectDirectory().equals(
668: FileOwnerQuery.getOwner(from).getProjectDirectory())) {
669: return;
670: }
671:
672: //#109580
673: if (SharabilityQuery.getSharability(FileUtil.toFile(from)) == SharabilityQuery.NOT_SHARABLE) {
674: return;
675: }
676:
677: if (from.isFolder()) {
678: FileObject copy = toParent.createFolder(from.getNameExt());
679: for (FileObject kid : from.getChildren()) {
680: doCopy(original, kid, copy);
681: }
682: } else {
683: assert from.isData();
684: FileObject target = FileUtil.copyFile(from, toParent, from
685: .getName(), from.getExt());
686: }
687: }
688:
689: private static FileObject createFolder(File parent, String name)
690: throws IOException {
691: FileObject path = FileUtil.toFileObject(parent);
692: if (path != null) {
693: return path.createFolder(name);
694: } else {
695: return createFolder(parent.getParentFile(),
696: parent.getName()).createFolder(name);
697: }
698: }
699:
700: private static boolean doDelete(Project original,
701: FileObject toDelete) throws IOException {
702: if (!original.getProjectDirectory()
703: .equals(
704: FileOwnerQuery.getOwner(toDelete)
705: .getProjectDirectory())) {
706: return false;
707: }
708:
709: if (toDelete.isFolder()) {
710: boolean delete = true;
711:
712: for (FileObject kid : toDelete.getChildren()) {
713: delete &= doDelete(original, kid);
714: }
715:
716: if (delete) {
717: //#83958
718: DataFolder.findFolder(toDelete).delete();
719: }
720:
721: return delete;
722: } else {
723: assert toDelete.isData();
724: try {
725: //#83958
726: DataObject dobj = DataObject.find(toDelete);
727: dobj.delete();
728: } catch (DataObjectNotFoundException ex) {
729: //In case of MultiDataObjects the file may be laready deleted
730: if (toDelete.isValid()) {
731: toDelete.delete();
732: }
733: }
734: return true;
735: }
736: }
737:
738: private static JComponent wrapPanel(JComponent component) {
739: component.setBorder(new EmptyBorder(12, 12, 12, 12));
740:
741: return component;
742: }
743:
744: private static void showConfirmationDialog(final JComponent panel,
745: Project project, String caption, String confirmButton,
746: String cancelButton, boolean doSetMessageType,
747: final Executor executor) {
748: final JButton confirm = new JButton();
749: Mnemonics.setLocalizedText(confirm, NbBundle.getMessage(
750: DefaultProjectOperationsImplementation.class, "LBL_"
751: + confirmButton));
752: final JButton cancel = new JButton(
753: cancelButton == null ? NbBundle.getMessage(
754: DefaultProjectOperationsImplementation.class,
755: "LBL_Cancel_Button") : NbBundle.getMessage(
756: DefaultProjectOperationsImplementation.class,
757: "LBL_" + cancelButton));
758:
759: confirm.getAccessibleContext().setAccessibleDescription(
760: NbBundle.getMessage(
761: DefaultProjectOperationsImplementation.class,
762: "ACSD_" + confirmButton));
763: cancel.getAccessibleContext().setAccessibleDescription(
764: cancelButton == null ? NbBundle.getMessage(
765: DefaultProjectOperationsImplementation.class,
766: "ACSD_Cancel_Button") : NbBundle.getMessage(
767: DefaultProjectOperationsImplementation.class,
768: "ACSD_" + cancelButton));
769:
770: assert panel instanceof InvalidablePanel;
771:
772: ((InvalidablePanel) panel)
773: .addChangeListener(new ChangeListener() {
774: public void stateChanged(ChangeEvent e) {
775: confirm.setEnabled(((InvalidablePanel) panel)
776: .isPanelValid());
777: }
778: });
779:
780: confirm.setEnabled(((InvalidablePanel) panel).isPanelValid());
781:
782: final Dialog[] dialog = new Dialog[1];
783:
784: DialogDescriptor dd = new DialogDescriptor(
785: doSetMessageType ? panel : wrapPanel(panel), caption,
786: true, new Object[] { confirm, cancel },
787: cancelButton != null ? cancel : confirm,
788: DialogDescriptor.DEFAULT_ALIGN, null,
789: new ActionListener() {
790: private boolean operationRunning;
791:
792: public void actionPerformed(ActionEvent e) {
793: //#65634: making sure that the user cannot close the dialog before the operation is finished:
794: if (operationRunning) {
795: return;
796: }
797:
798: if (dialog[0] instanceof JDialog) {
799: ((JDialog) dialog[0])
800: .getRootPane()
801: .getInputMap(
802: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
803: .remove(
804: KeyStroke.getKeyStroke(
805: KeyEvent.VK_ESCAPE,
806: 0));
807: ((JDialog) dialog[0])
808: .setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
809: }
810:
811: operationRunning = true;
812:
813: if (e.getSource() == confirm) {
814: confirm.setEnabled(false);
815: cancel.setEnabled(false);
816: ((InvalidablePanel) panel).showProgress();
817:
818: Component findParent = panel;
819:
820: while (findParent != null
821: && !(findParent instanceof Window)) {
822: findParent = findParent.getParent();
823: }
824:
825: if (findParent != null) {
826: ((Window) findParent).pack();
827: }
828:
829: RequestProcessor.getDefault().post(
830: new Runnable() {
831: public void run() {
832: Exception e = null;
833:
834: try {
835: executor.execute();
836: } catch (Exception ex) {
837: e = ex;
838: }
839:
840: final Exception ex = e;
841:
842: SwingUtilities
843: .invokeLater(new Runnable() {
844: public void run() {
845: dialog[0]
846: .setVisible(false);
847:
848: if (ex != null) {
849: ErrorManager
850: .getDefault()
851: .notify(
852: ErrorManager.INFORMATIONAL,
853: ex);
854: ErrorManager
855: .getDefault()
856: .notify(
857: ErrorManager.USER,
858: ex);
859: }
860: }
861: });
862: }
863: });
864: } else {
865: dialog[0].setVisible(false);
866: }
867: }
868: });
869:
870: if (doSetMessageType) {
871: dd.setMessageType(NotifyDescriptor.QUESTION_MESSAGE);
872: }
873:
874: dd.setClosingOptions(new Object[0]);
875:
876: dialog[0] = DialogDisplayer.getDefault().createDialog(dd);
877:
878: dialog[0].setVisible(true);
879:
880: dialog[0].dispose();
881: dialog[0] = null;
882: }
883:
884: static String computeError(File location, String projectNameText,
885: boolean pureRename) {
886: return computeError(location, projectNameText, null, pureRename);
887: }
888:
889: static String computeError(File location, String projectNameText,
890: String projectFolderText, boolean pureRename) {
891: File parent = location;
892: if (!location.exists()) {
893: //if some dirs in teh chain are not created, consider it ok.
894: parent = location.getParentFile();
895: while (parent != null && !parent.exists()) {
896: parent = parent.getParentFile();
897: }
898: if (parent == null) {
899: return NbBundle.getMessage(
900: DefaultProjectOperationsImplementation.class,
901: "ERR_Location_Does_Not_Exist");
902: }
903: }
904:
905: if (!parent.canWrite()) {
906: return NbBundle.getMessage(
907: DefaultProjectOperationsImplementation.class,
908: "ERR_Location_Read_Only");
909: }
910:
911: if (projectNameText.length() == 0) {
912: return NbBundle.getMessage(
913: DefaultProjectOperationsImplementation.class,
914: "ERR_Project_Name_Must_Entered");
915: }
916:
917: File projectFolderFile = null;
918: if (projectFolderText == null) {
919: projectFolderFile = new File(location, projectNameText);
920: } else {
921: projectFolderFile = new File(projectFolderText);
922: }
923:
924: if (projectFolderFile.exists() && !pureRename) {
925: return NbBundle.getMessage(
926: DefaultProjectOperationsImplementation.class,
927: "ERR_Project_Folder_Exists");
928: }
929:
930: if (projectNameText.indexOf('/') != -1
931: || projectNameText.indexOf('\\') != -1) {
932: return NbBundle.getMessage(
933: DefaultProjectOperationsImplementation.class,
934: "ERR_Not_Valid_Filename", projectNameText);
935: }
936:
937: return null;
938: }
939:
940: private static void close(final Project prj) {
941: Mutex.EVENT.readAccess(new Mutex.Action<Void>() {
942: public Void run() {
943: LifecycleManager.getDefault().saveAll();
944:
945: Action closeAction = CommonProjectActions
946: .closeProjectAction();
947: closeAction = closeAction instanceof ContextAwareAction ? ((ContextAwareAction) closeAction)
948: .createContextAwareInstance(Lookups.fixed(prj))
949: : null;
950:
951: if (closeAction != null && closeAction.isEnabled()) {
952: closeAction.actionPerformed(new ActionEvent(prj,
953: -1, "")); // NOI18N
954: } else {
955: //fallback:
956: OpenProjects.getDefault().close(
957: new Project[] { prj });
958: }
959:
960: return null;
961: }
962: });
963: }
964:
965: private static void open(final Project prj, final boolean setAsMain) {
966: Mutex.EVENT.readAccess(new Runnable() {
967: public void run() {
968: OpenProjects.getDefault().open(new Project[] { prj },
969: false);
970: if (setAsMain) {
971: OpenProjects.getDefault().setMainProject(prj);
972: }
973: }
974: });
975: }
976:
977: static interface Executor {
978: public void execute() throws Exception;
979: }
980:
981: public static interface InvalidablePanel {
982: public void addChangeListener(ChangeListener l);
983:
984: public void removeChangeListener(ChangeListener l);
985:
986: public boolean isPanelValid();
987:
988: public void showProgress();
989: }
990: //</editor-fold>
991:
992: }
|