/*
* Copyright 2015 Igor Maznitsa.
*
* Licensed 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 com.igormaznitsa.nbmindmap.utils;
import com.igormaznitsa.nbmindmap.nb.swing.FileEditPanel;
import com.igormaznitsa.nbmindmap.nb.swing.UriEditPanel;
import com.igormaznitsa.nbmindmap.nb.swing.PlainTextEditor;
import com.igormaznitsa.nbmindmap.nb.swing.ColorChooserButton;
import com.igormaznitsa.mindmap.model.MMapURI;
import com.igormaznitsa.mindmap.model.Topic;
import com.igormaznitsa.mindmap.model.logger.Logger;
import com.igormaznitsa.mindmap.model.logger.LoggerFactory;
import static com.igormaznitsa.mindmap.swing.panel.utils.Utils.html2color;
import com.igormaznitsa.nbmindmap.nb.explorer.MMKnowledgeSources;
import com.igormaznitsa.nbmindmap.nb.options.MMDCfgOptionsPanelController;
import java.awt.Color;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.event.ActionEvent;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.prefs.Preferences;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.swing.Action;
import javax.swing.JComponent;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import org.netbeans.api.project.Project;
import org.netbeans.api.project.ProjectUtils;
import org.netbeans.api.project.SourceGroup;
import org.netbeans.api.project.Sources;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.awt.HtmlBrowser;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.loaders.DataObject;
import org.openide.loaders.DataObjectNotFoundException;
import org.openide.nodes.Node;
import org.openide.util.ContextAwareAction;
import org.openide.util.Lookup;
import org.openide.util.NbPreferences;
import org.openide.util.datatransfer.ExClipboard;
import org.openide.util.lookup.Lookups;
import com.igormaznitsa.meta.annotation.MustNotContainNull;
import com.igormaznitsa.meta.common.utils.Assertions;
public final class NbUtils {
// List of all found types of source groups in NetBeans sources.
private static final List<String> ALL_KNOWN_SCOPE_TYPES = Arrays.asList("Resources", "TestResources", "GeneratedSources", "java", "resources", "main", "test", "doc_root", "web_inf", "PHPSOURCE", "groovy", "grails", "grails_unknown", "HTML5-Sources", "HTML5-Tests");
public enum SelectIn {
PROJECTS("org-netbeans-modules-project-ui-SelectInProjects.instance"),
FILES("org-netbeans-modules-project-ui-SelectInFiles.instance"),
FAVORITES("org-netbeans-modules-favorites-Select.instance");
private final String actionName;
private SelectIn (@Nonnull final String actionInstance) {
this.actionName = actionInstance;
}
public boolean select (@Nullable final Object source, @Nonnull final Object object) {
boolean result = false;
final Action action = FileUtil.getConfigObject("Actions/Window/SelectDocumentNode/" + actionName, ContextAwareAction.class); //NOI18N
if (action != null) {
try {
switch (this) {
case FAVORITES: {
final Node node = extractNode(object);
if (node != null) {
final Action contextAction = ((ContextAwareAction) action).createContextAwareInstance(Lookups.singleton(node));
contextAction.actionPerformed(new ActionEvent(source, ActionEvent.ACTION_PERFORMED, null));
result = true;
}
}
break;
case FILES:
case PROJECTS: {
final Action contextAction = ((ContextAwareAction) action).createContextAwareInstance(Lookups.singleton(object));
contextAction.actionPerformed(new ActionEvent(source, ActionEvent.ACTION_PERFORMED, null));
result = true;
}
break;
default:
throw new Error("Unexpected type " + this);
}
}
catch (Exception ex) {
LOGGER.error("Error during SelectIn(" + this.name() + ')', ex);
}
}
return result;
}
}
private static final Logger LOGGER = LoggerFactory.getLogger(NbUtils.class);
public static final MMapURI EMPTY_URI;
public static final boolean DARK_THEME;
static {
try {
EMPTY_URI = new MMapURI("http://igormaznitsa.com/specialuri#empty"); //NOI18N
}
catch (URISyntaxException ex) {
throw new Error("Unexpected exception", ex); //NOI18N
}
final Color color = UIManager.getColor("Panel.background");
if (color == null) {
DARK_THEME = false;
}
else {
DARK_THEME = calculateBrightness(color) < 150;
}
}
private NbUtils () {
}
@Nullable
public static DataObject extractDataObject (@Nullable final Object object) {
if (object instanceof DataObject) {
return (DataObject) object;
}
else if (object instanceof FileObject) {
try {
return DataObject.find((FileObject) object);
}
catch (DataObjectNotFoundException ex) {
return null;
}
}
else if (object instanceof Node) {
return ((Node) object).getLookup().lookup(DataObject.class);
}
else if (object instanceof Lookup) {
return ((Lookup) object).lookup(DataObject.class);
}
return null;
}
@Nullable
public static FileObject extractFileObject (@Nullable final Object object) {
if (object instanceof DataObject) {
return ((DataObject) object).getPrimaryFile();
}
else if (object instanceof FileObject) {
return (FileObject) object;
}
else if (object instanceof Node) {
final DataObject dobj = extractDataObject(object);
return dobj == null ? ((Node) object).getLookup().lookup(FileObject.class) : dobj.getPrimaryFile();
}
else if (object instanceof Lookup) {
return ((Lookup) object).lookup(FileObject.class);
}
return null;
}
@Nullable
public static Node extractNode (@Nullable final Object object) {
if (object instanceof DataObject) {
return ((DataObject) object).getNodeDelegate();
}
else if (object instanceof FileObject) {
try {
final DataObject dobj = DataObject.find((FileObject) object);
return dobj == null ? null : dobj.getNodeDelegate();
}
catch (DataObjectNotFoundException ex) {
return null;
}
}
else if (object instanceof Node) {
return (Node) object;
}
else if (object instanceof Lookup) {
return ((Lookup) object).lookup(Node.class);
}
else if (object instanceof Project) {
try {
final DataObject dobj = DataObject.find(((Project) object).getProjectDirectory());
return dobj.getNodeDelegate();
}
catch (DataObjectNotFoundException ex) {
// to do nothing
}
}
return null;
}
public static int calculateBrightness (@Nonnull final Color color) {
return (int) Math.sqrt(
color.getRed() * color.getRed() * .241d
+ color.getGreen() * color.getGreen() * .691d
+ color.getBlue() * color.getBlue() * .068d);
}
@Nullable
public static Color extractCommonColorForColorChooserButton (@Nonnull final String colorAttribute, @Nonnull @MustNotContainNull final Topic[] topics) {
Color result = null;
for (final Topic t : topics) {
final Color color = html2color(t.getAttribute(colorAttribute), false);
if (result == null) {
result = color;
}
else if (!result.equals(color)) {
return ColorChooserButton.DIFF_COLORS;
}
}
return result;
}
@Nonnull
public static Preferences getPreferences () {
return NbPreferences.forModule(MMDCfgOptionsPanelController.class);
}
public static void msgError (@Nonnull final String text) {
DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(text, NotifyDescriptor.ERROR_MESSAGE));
}
public static void msgInfo (@Nonnull final String text) {
DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(text, NotifyDescriptor.INFORMATION_MESSAGE));
}
public static void msgWarn (@Nonnull final String text) {
DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(text, NotifyDescriptor.WARNING_MESSAGE));
}
public static boolean msgConfirmOkCancel (@Nonnull final String title, @Nonnull final String query) {
final NotifyDescriptor desc = new NotifyDescriptor.Confirmation(query, title, NotifyDescriptor.OK_CANCEL_OPTION);
final Object obj = DialogDisplayer.getDefault().notify(desc);
return NotifyDescriptor.OK_OPTION.equals(obj);
}
public static boolean msgConfirmYesNo (@Nonnull final String title, @Nonnull final String query) {
final NotifyDescriptor desc = new NotifyDescriptor.Confirmation(query, title, NotifyDescriptor.YES_NO_OPTION);
final Object obj = DialogDisplayer.getDefault().notify(desc);
return NotifyDescriptor.YES_OPTION.equals(obj);
}
@Nullable
public static Boolean msgConfirmYesNoCancel (@Nonnull final String title, @Nonnull final String query) {
final NotifyDescriptor desc = new NotifyDescriptor.Confirmation(query, title, NotifyDescriptor.YES_NO_CANCEL_OPTION);
final Object obj = DialogDisplayer.getDefault().notify(desc);
if (NotifyDescriptor.CANCEL_OPTION.equals(obj)) {
return null;
}
return NotifyDescriptor.YES_OPTION.equals(obj);
}
public static boolean msgComponentOkCancel (@Nonnull final String title, @Nonnull final JComponent component) {
final NotifyDescriptor desc = new NotifyDescriptor.Confirmation(component, title, NotifyDescriptor.OK_CANCEL_OPTION, NotifyDescriptor.PLAIN_MESSAGE);
return DialogDisplayer.getDefault().notify(desc) == NotifyDescriptor.OK_OPTION;
}
public static void msgInfo (@Nonnull final JComponent component) {
DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(component, NotifyDescriptor.INFORMATION_MESSAGE));
}
public static boolean plainMessageOkCancel (@Nonnull final String title, @Nonnull final JComponent compo) {
final NotifyDescriptor desc = new NotifyDescriptor.Confirmation(compo, title, NotifyDescriptor.OK_CANCEL_OPTION, NotifyDescriptor.PLAIN_MESSAGE);
return DialogDisplayer.getDefault().notify(desc) == NotifyDescriptor.OK_OPTION;
}
public static void plainMessageOk (@Nonnull final String title, @Nonnull final JComponent compo) {
final NotifyDescriptor desc = new NotifyDescriptor.Message(compo, NotifyDescriptor.PLAIN_MESSAGE);
desc.setTitle(title);
DialogDisplayer.getDefault().notify(desc);
}
@Nullable
public static String editText (@Nonnull final String title, @Nonnull final String text) {
final PlainTextEditor textEditor = new PlainTextEditor(text);
try {
if (plainMessageOkCancel(title, textEditor)) {
return textEditor.getText();
}
else {
return null;
}
}
finally {
textEditor.dispose();
}
}
@Nullable
public static MMapURI editURI (@Nonnull final String title, @Nullable final MMapURI uri) {
final UriEditPanel textEditor = new UriEditPanel(uri == null ? null : uri.asString(false, false));
textEditor.doLayout();
textEditor.setPreferredSize(new Dimension(450, textEditor.getPreferredSize().height));
final NotifyDescriptor desc = new NotifyDescriptor.Confirmation(textEditor, title, NotifyDescriptor.OK_CANCEL_OPTION, NotifyDescriptor.PLAIN_MESSAGE);
if (DialogDisplayer.getDefault().notify(desc) == NotifyDescriptor.OK_OPTION) {
final String text = textEditor.getText();
if (text.isEmpty()) {
return EMPTY_URI;
}
try {
return new MMapURI(text.trim());
}
catch (URISyntaxException ex) {
msgError(String.format(java.util.ResourceBundle.getBundle("com/igormaznitsa/nbmindmap/i18n/Bundle").getString("NbUtils.errMsgIllegalURI"), text));
return null;
}
}
else {
return null;
}
}
@Nullable
public static FileEditPanel.DataContainer editFilePath (@Nonnull final String title, @Nullable final File projectFolder, @Nullable final FileEditPanel.DataContainer data) {
final FileEditPanel filePathEditor = new FileEditPanel(projectFolder, data);
filePathEditor.doLayout();
filePathEditor.setPreferredSize(new Dimension(450, filePathEditor.getPreferredSize().height));
final NotifyDescriptor desc = new NotifyDescriptor.Confirmation(filePathEditor, title, NotifyDescriptor.OK_CANCEL_OPTION, NotifyDescriptor.PLAIN_MESSAGE);
FileEditPanel.DataContainer result = null;
if (DialogDisplayer.getDefault().notify(desc) == NotifyDescriptor.OK_OPTION) {
result = filePathEditor.getData();
if (!result.isValid()) {
NbUtils.msgError(String.format(java.util.ResourceBundle.getBundle("com/igormaznitsa/nbmindmap/i18n/Bundle").getString("MMDGraphEditor.editFileLinkForTopic.errorCantFindFile"), result.getPath()));
result = null;
}
}
return result;
}
public static boolean browseURI (@Nonnull final URI uri, final boolean preferInsideBrowserIfPossible) {
try {
if (preferInsideBrowserIfPossible) {
HtmlBrowser.URLDisplayer.getDefault().showURL(uri.toURL());
}
else {
HtmlBrowser.URLDisplayer.getDefault().showURLExternal(uri.toURL());
}
return true;
}
catch (MalformedURLException ex) {
LOGGER.error("MalformedURLException", ex); //NOI18N
return false;
}
}
public static void openInSystemViewer (@Nonnull final File file) {
final Runnable startEdit = new Runnable() {
@Override
public void run () {
boolean ok = false;
if (Desktop.isDesktopSupported()) {
final Desktop dsk = Desktop.getDesktop();
if (dsk.isSupported(Desktop.Action.OPEN)) {
try {
dsk.open(file);
ok = true;
}
catch (Throwable ex) {
LOGGER.error("Can't open file in system viewer : " + file, ex);//NOI18N
}
}
}
if (!ok) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run () {
NbUtils.msgError("Can't open file in system viewer! See the log!");//NOI18N
Toolkit.getDefaultToolkit().beep();
}
});
}
}
};
final Thread thr = new Thread(startEdit, " MMDStartFileEdit");//NOI18N
thr.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException (final Thread t, final Throwable e) {
LOGGER.error("Detected uncaught exception in openInSystemViewer() for file " + file, e);
}
});
thr.setDaemon(true);
thr.start();
}
@Nonnull
@MustNotContainNull
public static Collection<SourceGroup> findAllSourceGroups (@Nonnull final Project project) {
final Sources sources = ProjectUtils.getSources(project);
final Set<SourceGroup> result = new HashSet<SourceGroup>();
for (final String scopeType : ALL_KNOWN_SCOPE_TYPES) {
for (final SourceGroup s : sources.getSourceGroups(scopeType)) {
result.add(s);
}
}
if (result.isEmpty() && !project.getClass().getName().equals("org.netbeans.modules.maven.NbMavenProjectImpl")) {
for (final SourceGroup s : sources.getSourceGroups(Sources.TYPE_GENERIC)) {
result.add(s);
}
}
return result;
}
public static boolean isInProjectKnowledgeFolder (@Nonnull final Project project, @Nonnull final FileObject file) {
final FileObject projectKnowledgeFolder = MMKnowledgeSources.findProjectKnowledgeFolder(project);
return projectKnowledgeFolder != null && FileUtil.isParentOf(projectKnowledgeFolder, file);
}
public static boolean isFileInProjectScope (@Nonnull final Project project, @Nonnull final FileObject file) {
final FileObject projectFolder = project.getProjectDirectory();
if (FileUtil.isParentOf(projectFolder, file)) {
for (final SourceGroup srcGroup : findAllSourceGroups(project)) {
final FileObject root = srcGroup.getRootFolder();
if (root != null) {
if (FileUtil.isParentOf(root, file)) {
return true;
}
}
}
return false;
}
else {
return false;
}
}
@Nonnull
public static Clipboard findClipboard() {
Clipboard result = Lookup.getDefault().lookup(ExClipboard.class);
if (result == null) {
result = Toolkit.getDefaultToolkit().getSystemClipboard();
}
return Assertions.assertNotNull("Clipbard is not found", result);
}
}