Package scratch.ide

Source Code of scratch.ide.Ide$ClipboardListener

/*
* 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 scratch.ide;

import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileEditor.FileEditorManagerAdapter;
import com.intellij.openapi.fileEditor.FileEditorManagerEvent;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.fileEditor.impl.NonProjectFileWritingAccessProvider;
import com.intellij.openapi.ide.CopyPasteManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.project.ProjectManagerAdapter;
import com.intellij.openapi.ui.InputValidatorEx;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.NotNullLazyKey;
import com.intellij.openapi.util.UserDataHolder;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.messages.MessageBusConnection;
import com.intellij.util.ui.UIUtil;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import scratch.Answer;
import scratch.MrScratchManager;
import scratch.Scratch;
import scratch.ScratchConfig;
import scratch.filesystem.FileSystem;
import scratch.ide.popup.ScratchListPopup;
import scratch.ide.popup.ScratchListPopupStep;

import javax.swing.*;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;

import static com.intellij.openapi.fileEditor.FileEditorManagerListener.FILE_EDITOR_MANAGER;
import static com.intellij.openapi.fileEditor.impl.NonProjectFileWritingAccessProvider.AccessStatus.ALLOWED;
import static java.awt.datatransfer.DataFlavor.stringFlavor;
import static scratch.ScratchConfig.AppendType.APPEND;
import static scratch.ScratchConfig.AppendType.PREPEND;
import static scratch.ide.ScratchComponent.mrScratchManager;
import static scratch.ide.Util.*;


public class Ide {
  private final FileSystem fileSystem;
  private final ScratchLog log;

  private int scratchListSelectedIndex;


  public Ide(FileSystem fileSystem, ScratchLog log) {
    this.fileSystem = fileSystem;
    this.log = log;
  }

  public void persistConfig(ScratchConfig config) {
    ScratchConfigPersistence.getInstance().updateFrom(config);
  }

  public void displayScratchesListPopup(List<Scratch> scratches, final UserDataHolder userDataHolder) {
    ScratchListPopupStep popupStep = new ScratchListPopupStep(scratches, takeProjectFrom(userDataHolder));
    popupStep.setDefaultOptionIndex(scratchListSelectedIndex);
    ScratchListPopup popup = new ScratchListPopup(popupStep) {

      @Override protected void onNewScratch() {
        SwingUtilities.invokeLater(new Runnable() {
          @Override public void run() {
            mrScratchManager().userWantsToEnterNewScratchName(userDataHolder);
          }
        });
      }

      @Override protected void onRenameScratch(final Scratch scratch) {
        SwingUtilities.invokeLater(new Runnable() {
          @Override public void run() {
            mrScratchManager().userWantsToEditScratchName(scratch);
          }
        });
      }

      @Override protected void onScratchDelete(Scratch scratch) {
        mrScratchManager().userAttemptedToDeleteScratch(scratch);
      }

            @Override protected void onScratchDeleteWithoutPrompt(Scratch scratch) {
                mrScratchManager().userWantsToDeleteScratch(scratch);
            }

      @Override protected void onScratchMoved(Scratch scratch, int shift) {
        mrScratchManager().userMovedScratch(scratch, shift);
      }

      @Override public void dispose() {
        scratchListSelectedIndex = getSelectedIndex();
        super.dispose();
      }
    };
    popup.showCenteredInCurrentWindow(takeProjectFrom(userDataHolder));
  }

  public void openScratch(Scratch scratch, UserDataHolder userDataHolder) {
    Project project = takeProjectFrom(userDataHolder);

    VirtualFile file = fileSystem.virtualFileBy(scratch.asFileName());
    if (file != null) {
      new OpenFileDescriptor(project, file).navigate(true);
    } else {
      log.failedToFindVirtualFileFor(scratch);
    }
  }

  public void openNewScratchDialog(String suggestedScratchName, UserDataHolder userDataHolder) {
    String message = "Scratch name (you can use '&' for mnemonics):";
    String scratchName = Messages.showInputDialog(message, "New Scratch", NO_ICON, suggestedScratchName, new InputValidatorEx() {
      @Override public boolean checkInput(String scratchName) {
        return ScratchComponent.mrScratchManager().checkIfUserCanCreateScratchWithName(scratchName).isYes;
      }

      @Nullable @Override public String getErrorText(String scratchName) {
        Answer answer = ScratchComponent.mrScratchManager().checkIfUserCanCreateScratchWithName(scratchName);
        return answer.explanation;
      }

      @Override public boolean canClose(String inputString) {
        return true;
      }
    });
    if (scratchName == null) return;

    mrScratchManager().userWantsToAddNewScratch(scratchName, userDataHolder);
  }

  public void addTextTo(Scratch scratch, final String clipboardText, final ScratchConfig.AppendType appendType) {
    VirtualFile virtualFile = fileSystem.virtualFileBy(scratch.asFileName());
    if (virtualFile == null) {
      log.failedToFindVirtualFileFor(scratch);
      return;
    }
    final Document document = FileDocumentManager.getInstance().getDocument(virtualFile);
    if (document == null) return;
    if (hasFocusInEditor(document)) return;

    ApplicationManager.getApplication().runWriteAction(new Runnable() {
      @Override public void run() {
        String text = document.getText();
        String newText;
        if (appendType == APPEND) {
          if (text.endsWith("\n")) {
            newText = text + clipboardText;
          } else {
            newText = text + "\n" + clipboardText;
          }
        } else if (appendType == PREPEND) {
          newText = clipboardText + "\n" + text;
        } else {
          throw new IllegalStateException();
        }

        document.setText(newText);
        FileDocumentManager.getInstance().saveDocument(document);
      }
    });
  }

  public void showRenameDialogFor(Scratch scratch) {
    String initialValue = scratch.fullNameWithMnemonics;
    String message = "Scratch name (you can use '&' for mnemonics):";
    String newScratchName = Messages.showInputDialog(message, "Scratch Rename", NO_ICON, initialValue, new ScratchListPopup.ScratchNameValidator(scratch));

    if (newScratchName != null) {
      mrScratchManager().userWantsToRename(scratch, newScratchName);
    }
  }

  public void showDeleteDialogFor(Scratch scratch) {
    String message = "Do you want to delete '" + scratch.name + "'?\n(This operation cannot be undone)";
    int userAnswer = Messages.showOkCancelDialog(message, "Delete Scratch", "&Delete", "&Cancel", UIUtil.getQuestionIcon());
    if (userAnswer != Messages.OK) return;

    mrScratchManager().userWantsToDeleteScratch(scratch);
  }


  /**
   *  Note that it's possible to turn on clipboard listener and forget about it
   *  with it appending clipboard content to default scratch forever.
   *
   *  Assume that notification on plugin start is good enough to remind user about clipboard listener.
   */
  public static class ClipboardListener {
    private static final Logger LOG = Logger.getInstance(ClipboardListener.class);

    private final MrScratchManager mrScratchManager;

    public ClipboardListener(MrScratchManager mrScratchManager) {
      this.mrScratchManager = mrScratchManager;
    }

    public void startListening() {
      CopyPasteManager.getInstance().addContentChangedListener(new CopyPasteManager.ContentChangedListener() {
        @Override
        public void contentChanged(@Nullable Transferable oldTransferable, Transferable newTransferable) {
          if (!mrScratchManager.shouldListenToClipboard()) return;

          try {
            String oldClipboard = null;
            if (oldTransferable != null) {
              Object transferData = oldTransferable.getTransferData(stringFlavor);
              oldClipboard = (transferData == null ? null : transferData.toString());
            }
            String clipboard = null;
            if (newTransferable != null) {
              Object transferData = newTransferable.getTransferData(stringFlavor);
              clipboard = (transferData == null ? null : transferData.toString());
            }
            if (clipboard == null || StringUtils.equals(oldClipboard, clipboard)) return;

            mrScratchManager.clipboardListenerWantsToAddTextToScratch(clipboard);

          } catch (UnsupportedFlavorException e) {
            LOG.info(e);
          } catch (IOException e) {
            LOG.info(e);
          }
        }
      });
    }
  }

  public static class OpenEditorTracker {
    private final MrScratchManager mrScratchManager;
    private final FileSystem fileSystem;
    // use WeakHashMap "just in case" to avoid keeping project references
    private final Map<Project, MessageBusConnection> connectionsByProject = new WeakHashMap<Project, MessageBusConnection>();

    public OpenEditorTracker(MrScratchManager mrScratchManager, FileSystem fileSystem) {
      this.mrScratchManager = mrScratchManager;
      this.fileSystem = fileSystem;
    }

    public OpenEditorTracker startTracking() {
      ProjectManager.getInstance().addProjectManagerListener(new ProjectManagerAdapter() {
        @Override public void projectOpened(final Project project) {
                    MessageBusConnection connection = project.getMessageBus().connect();
                    connection.subscribe(FILE_EDITOR_MANAGER, new FileEditorManagerAdapter() {
                        @Override
                        public void selectionChanged(@NotNull FileEditorManagerEvent event) {
                            VirtualFile virtualFile = event.getNewFile();
                            if (virtualFile == null) return;

                            if (fileSystem.isScratch(virtualFile)) {
                                allowAccessToNonProjectFile_HACK(virtualFile, project);
                                mrScratchManager.userOpenedScratch(virtualFile.getName());
                            }
                        }
                    });
                    connectionsByProject.put(project, connection);
                }

        @Override public void projectClosed(Project project) {
          MessageBusConnection connection = connectionsByProject.remove(project);
          if (connection != null) connection.disconnect();
        }
      });
      return this;
    }

        private static void allowAccessToNonProjectFile_HACK(VirtualFile virtualFile, Project project) {
            try {
                Field field = findAccessStatusField();
                if (field == null) return;

                field.setAccessible(true);
                @SuppressWarnings("unchecked")
                NotNullLazyKey<Map<VirtualFile, NonProjectFileWritingAccessProvider.AccessStatus>, Project> map =
                        (NotNullLazyKey<Map<VirtualFile, NonProjectFileWritingAccessProvider.AccessStatus>, Project>) field.get(null);

                map.getValue(project).put(virtualFile, ALLOWED);

            } catch (IllegalAccessException ignore) {
            }
        }

        private static Field findAccessStatusField() {
            try {
                return NonProjectFileWritingAccessProvider.class.getDeclaredField("ACCESS_STATUS");
            } catch (NoSuchFieldException e) {
                // searching by type because field name might be obfuscated (e.g. seen in PhpStorm)
                for (Field field : NonProjectFileWritingAccessProvider.class.getDeclaredFields()) {
                    if (field.getType().equals(NotNullLazyKey.class)) return field;
                }
                return null;
            }
        }
    }
}
TOP

Related Classes of scratch.ide.Ide$ClipboardListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.