editor remote file improved: refresh and jump to source actions added, url field added

This commit is contained in:
nik
2009-12-28 11:08:26 +03:00
parent 70c505b519
commit cc842765e8
21 changed files with 591 additions and 109 deletions
@@ -0,0 +1,102 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* 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.intellij.openapi.fileEditor.impl.http;
import com.intellij.CommonBundle;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ui.util.CellAppearanceUtils;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.popup.PopupChooserBuilder;
import com.intellij.openapi.util.IconLoader;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.impl.http.HttpVirtualFile;
import com.intellij.openapi.vfs.impl.http.RemoteFileState;
import com.intellij.psi.search.FilenameIndex;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.ui.ColoredListCellRenderer;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.util.Collection;
/**
* @author nik
*/
public class JumpFromRemoteFileToLocalAction extends AnAction {
private HttpVirtualFile myFile;
private final Project myProject;
public JumpFromRemoteFileToLocalAction(HttpVirtualFile file, Project project) {
super("Find Local File", "", IconLoader.findIcon("/general/autoscrollToSource.png"));
myFile = file;
myProject = project;
}
@Override
public void update(AnActionEvent e) {
final RemoteFileState state = myFile.getFileInfo().getState();
e.getPresentation().setEnabled(state == RemoteFileState.DOWNLOADED);
}
@Override
public void actionPerformed(AnActionEvent e) {
final String url = myFile.getUrl();
for (LocalFileFinder finder : LocalFileFinder.EP_NAME.getExtensions()) {
final VirtualFile file = finder.findLocalFile(url, myProject);
if (file != null) {
navigateToFile(myProject, file);
}
}
final Collection<VirtualFile> files =
FilenameIndex.getVirtualFilesByName(myProject, myFile.getName(), GlobalSearchScope.allScope(myProject));
if (files.isEmpty()) {
Messages.showErrorDialog(myProject, "Cannot find local file for '" + url + "'", CommonBundle.getErrorTitle());
return;
}
if (files.size() == 1) {
navigateToFile(myProject, ContainerUtil.getFirstItem(files, null));
}
else {
final JList list = new JList(files.toArray(new VirtualFile[files.size()]));
list.setCellRenderer(new ColoredListCellRenderer() {
@Override
protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) {
CellAppearanceUtils.forVirtualFile((VirtualFile)value).customize(this);
}
});
new PopupChooserBuilder(list)
.setTitle("Select Target File")
.setMovable(true)
.setItemChoosenCallback(new Runnable() {
public void run() {
for (Object value : list.getSelectedValues()) {
navigateToFile(myProject, (VirtualFile)value);
}
}
}).createPopup().showUnderneathOf(e.getInputEvent().getComponent());
}
}
private static void navigateToFile(Project project, @NotNull VirtualFile file) {
new OpenFileDescriptor(project, file).navigate(true);
}
}
@@ -0,0 +1,32 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* 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.intellij.openapi.fileEditor.impl.http;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.impl.http.HttpVirtualFile;
import org.jetbrains.annotations.NotNull;
/**
* @author nik
*/
public class LangRemoteFileEditorActionProvider extends RemoteFileEditorActionProvider {
@NotNull
@Override
public AnAction[] createToolbarActions(@NotNull Project project, @NotNull HttpVirtualFile file) {
return new AnAction[] {new JumpFromRemoteFileToLocalAction(file, project)};
}
}
@@ -15,6 +15,7 @@
*/
package com.intellij.ide.actions;
import com.intellij.CommonBundle;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
@@ -40,11 +41,11 @@ public class OpenRemoteFileAction extends AnAction implements DumbAware {
public void actionPerformed(final AnActionEvent e) {
Project project = e.getData(PlatformDataKeys.PROJECT);
String defaultUrl = "http://localhost:8080/index.html";
String url = Messages.showInputDialog(project, "URL", "Open Remote File", null, defaultUrl, null);
String url = Messages.showInputDialog(project, "URL:", "Open Remote File", null, defaultUrl, null);
if (url != null) {
VirtualFile file = VirtualFileManager.getInstance().findFileByUrl(url);
if (file == null) {
Messages.showErrorDialog(project, "Cannot find file '" + url + "'", "Cannot Open File");
Messages.showErrorDialog(project, "Cannot find file '" + url + "'", CommonBundle.getErrorTitle());
}
else {
FileEditorManager.getInstance(project).openFile(file, true);
@@ -30,6 +30,7 @@ import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.keymap.ex.KeymapManagerEx;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.impl.http.HttpVirtualFile;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -201,7 +202,7 @@ public class ContextMenuImpl extends JPanel implements Disposable {
}
final VirtualFile file = FileDocumentManager.getInstance().getFile(document);
return file != null && file.isValid() && file.getFileSystem() == LocalFileSystem.getInstance();
return file != null && file.isValid() && (file.getFileSystem() == LocalFileSystem.getInstance() || file instanceof HttpVirtualFile);
}
private void scheduleHide() {
@@ -17,15 +17,15 @@ package com.intellij.openapi.fileEditor.impl.http;
import com.intellij.codeHighlighting.BackgroundEditorHighlighter;
import com.intellij.ide.structureView.StructureViewBuilder;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.FileEditorLocation;
import com.intellij.openapi.fileEditor.FileEditorState;
import com.intellij.openapi.fileEditor.FileEditorStateLevel;
import com.intellij.openapi.fileEditor.*;
import com.intellij.openapi.fileEditor.impl.text.TextEditorState;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.UserDataHolderBase;
import com.intellij.openapi.vfs.impl.http.HttpVirtualFile;
import com.intellij.openapi.project.Project;
import com.intellij.pom.Navigatable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.beans.PropertyChangeListener;
@@ -33,22 +33,25 @@ import java.beans.PropertyChangeListener;
/**
* @author nik
*/
public class HttpFileEditor extends UserDataHolderBase implements FileEditor {
private final HttpVirtualFile myVirtualFile;
private final DownloadRemoteFilePanel myDownloadPanel;
public class HttpFileEditor implements NavigatableFileEditor {
private final UserDataHolderBase myUserDataHolder = new UserDataHolderBase();
private final RemoteFilePanel myPanel;
public HttpFileEditor(final Project project, final HttpVirtualFile virtualFile) {
myVirtualFile = virtualFile;
myDownloadPanel = new DownloadRemoteFilePanel(project, virtualFile);
myPanel = new RemoteFilePanel(project, virtualFile);
}
@NotNull
public JComponent getComponent() {
return myDownloadPanel.getMainPanel();
return myPanel.getMainPanel();
}
public JComponent getPreferredFocusedComponent() {
return myDownloadPanel.getMainPanel();
final TextEditor textEditor = myPanel.getFileEditor();
if (textEditor != null) {
return textEditor.getPreferredFocusedComponent();
}
return myPanel.getMainPanel();
}
@NotNull
@@ -58,10 +61,18 @@ public class HttpFileEditor extends UserDataHolderBase implements FileEditor {
@NotNull
public FileEditorState getState(@NotNull final FileEditorStateLevel level) {
final TextEditor textEditor = myPanel.getFileEditor();
if (textEditor != null) {
return textEditor.getState(level);
}
return new TextEditorState();
}
public void setState(@NotNull final FileEditorState state) {
final TextEditor textEditor = myPanel.getFileEditor();
if (textEditor != null) {
textEditor.setState(state);
}
}
public boolean isModified() {
@@ -73,32 +84,79 @@ public class HttpFileEditor extends UserDataHolderBase implements FileEditor {
}
public void selectNotify() {
myDownloadPanel.showNotify();
myPanel.selectNotify();
}
public void deselectNotify() {
myDownloadPanel.hideNotify();
myPanel.deselectNotify();
}
public void addPropertyChangeListener(@NotNull final PropertyChangeListener listener) {
myPanel.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(@NotNull final PropertyChangeListener listener) {
myPanel.removePropertyChangeListener(listener);
}
public BackgroundEditorHighlighter getBackgroundHighlighter() {
final TextEditor textEditor = myPanel.getFileEditor();
if (textEditor != null) {
return textEditor.getBackgroundHighlighter();
}
return null;
}
public boolean canNavigateTo(@NotNull Navigatable navigatable) {
final TextEditor textEditor = myPanel.getFileEditor();
if (textEditor != null) {
return textEditor.canNavigateTo(navigatable);
}
return false;
}
public void navigateTo(@NotNull Navigatable navigatable) {
final TextEditor textEditor = myPanel.getFileEditor();
if (textEditor != null) {
textEditor.navigateTo(navigatable);
}
}
public <T> T getUserData(@NotNull Key<T> key) {
final TextEditor textEditor = myPanel.getFileEditor();
if (textEditor != null) {
return textEditor.getUserData(key);
}
return myUserDataHolder.getUserData(key);
}
public <T> void putUserData(@NotNull Key<T> key, @Nullable T value) {
final TextEditor textEditor = myPanel.getFileEditor();
if (textEditor != null) {
textEditor.putUserData(key, value);
}
else {
myUserDataHolder.putUserData(key, value);
}
}
public FileEditorLocation getCurrentLocation() {
final TextEditor textEditor = myPanel.getFileEditor();
if (textEditor != null) {
return textEditor.getCurrentLocation();
}
return null;
}
public StructureViewBuilder getStructureViewBuilder() {
final TextEditor textEditor = myPanel.getFileEditor();
if (textEditor != null) {
return textEditor.getStructureViewBuilder();
}
return null;
}
public void dispose() {
myDownloadPanel.dispose();
myPanel.dispose();
}
}
@@ -19,13 +19,11 @@ import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.FileEditorPolicy;
import com.intellij.openapi.fileEditor.FileEditorProvider;
import com.intellij.openapi.fileEditor.FileEditorState;
import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider;
import com.intellij.openapi.fileEditor.impl.text.TextEditorState;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.impl.http.HttpVirtualFile;
import com.intellij.openapi.vfs.impl.http.RemoteFileInfo;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
@@ -39,12 +37,7 @@ public class HttpFileEditorProvider implements FileEditorProvider {
@NotNull
public FileEditor createEditor(@NotNull final Project project, @NotNull final VirtualFile file) {
HttpVirtualFile virtualFile = (HttpVirtualFile)file;
RemoteFileInfo remoteFileInfo = virtualFile.getFileInfo();
if (remoteFileInfo.isDownloaded()) {
return TextEditorProvider.getInstance().createEditor(project, file);
}
return new HttpFileEditor(project, virtualFile);
return new HttpFileEditor(project, (HttpVirtualFile)file);
}
public void disposeEditor(@NotNull final FileEditor editor) {
@@ -0,0 +1,32 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* 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.intellij.openapi.fileEditor.impl.http;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author nik
*/
public abstract class LocalFileFinder {
public static final ExtensionPointName<LocalFileFinder> EP_NAME = ExtensionPointName.create("com.intellij.http.localFileFinder");
@Nullable
public abstract VirtualFile findLocalFile(@NotNull String url, @NotNull Project project);
}
@@ -0,0 +1,45 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* 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.intellij.openapi.fileEditor.impl.http;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.util.IconLoader;
import com.intellij.openapi.vfs.impl.http.HttpVirtualFile;
import com.intellij.openapi.vfs.impl.http.RemoteFileState;
/**
* @author nik
*/
public class RefreshRemoteFileAction extends AnAction {
private HttpVirtualFile myFile;
public RefreshRemoteFileAction(HttpVirtualFile file) {
super("Reload File", "", IconLoader.findIcon("/actions/sync.png"));
myFile = file;
}
@Override
public void update(AnActionEvent e) {
final RemoteFileState state = myFile.getFileInfo().getState();
e.getPresentation().setEnabled(state == RemoteFileState.DOWNLOADED || state == RemoteFileState.ERROR_OCCURRED);
}
@Override
public void actionPerformed(AnActionEvent e) {
myFile.refresh(true, false);
}
}
@@ -0,0 +1,33 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* 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.intellij.openapi.fileEditor.impl.http;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.impl.http.HttpVirtualFile;
import org.jetbrains.annotations.NotNull;
/**
* @author nik
*/
public abstract class RemoteFileEditorActionProvider {
public static final ExtensionPointName<RemoteFileEditorActionProvider> EP_NAME = ExtensionPointName.create("com.intellij.http.fileEditorActionProvider");
@NotNull
public abstract AnAction[] createToolbarActions(@NotNull Project project, @NotNull HttpVirtualFile file);
}
@@ -1,27 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.openapi.fileEditor.impl.http.DownloadRemoteFilePanel">
<grid id="27dc6" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="5" left="5" bottom="5" right="5"/>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.openapi.fileEditor.impl.http.RemoteFilePanel">
<grid id="27dc6" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="0">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="500" height="400"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<vspacer id="202db">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
<grid id="79431" binding="myContentPanel" layout-manager="CardLayout" hgap="0" vgap="0">
<constraints>
<grid row="0" column="0" row-span="2" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
<grid row="1" column="0" row-span="2" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<grid id="bf11e" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<grid id="bf11e" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="5" left="5" bottom="5" right="5"/>
<constraints>
<card name="downloading"/>
</constraints>
@@ -33,7 +28,7 @@
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Label"/>
<text value="Downloading started"/>
</properties>
</component>
<grid id="bea64" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
@@ -62,10 +57,15 @@
</component>
</children>
</grid>
<vspacer id="7bed">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
</children>
</grid>
<grid id="91a3a" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<grid id="91a3a" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="5" left="5" bottom="5" right="5"/>
<constraints>
<card name="error"/>
</constraints>
@@ -111,8 +111,49 @@
</component>
</children>
</grid>
<vspacer id="2573c">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
</children>
</grid>
<grid id="79221" binding="myEditorPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
<constraints>
<card name="editor"/>
</constraints>
<properties/>
<border type="none"/>
<children/>
</grid>
</children>
</grid>
<grid id="c789e" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="0" vgap="-1">
<margin top="3" left="3" bottom="0" right="3"/>
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="1" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="72bae" class="javax.swing.JTextField" binding="myUrlTextField">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties>
<editable value="false"/>
</properties>
</component>
<grid id="55596" binding="myToolbarPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children/>
</grid>
</children>
</grid>
</children>
@@ -15,34 +15,43 @@
*/
package com.intellij.openapi.fileEditor.impl.http;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileEditor.TextEditor;
import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.IconLoader;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.impl.http.FileDownloadingListener;
import com.intellij.openapi.vfs.impl.http.HttpVirtualFile;
import com.intellij.openapi.vfs.impl.http.RemoteFileInfo;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.vfs.impl.http.RemoteFileState;
import com.intellij.util.EventDispatcher;
import com.intellij.util.net.HTTPProxySettingsDialog;
import com.intellij.util.ui.update.MergingUpdateQueue;
import com.intellij.util.ui.update.Update;
import com.intellij.util.net.HTTPProxySettingsDialog;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
/**
* @author nik
*/
public class DownloadRemoteFilePanel {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.fileEditor.impl.http.DownloadRemoteFilePanel");
public class RemoteFilePanel implements PropertyChangeListener {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.fileEditor.impl.http.RemoteFilePanel");
private static final Icon ERROR_ICON = IconLoader.getIcon("/runConfigurations/configurationWarning.png");
@NonNls private static final String ERROR_CARD = "error";
@NonNls private static final String DOWNLOADING_CARD = "downloading";
@NonNls private static final String EDITOR_CARD = "editor";
private JPanel myMainPanel;
private JLabel myProgressLabel;
private JProgressBar myProgressBar;
@@ -51,16 +60,23 @@ public class DownloadRemoteFilePanel {
private JLabel myErrorLabel;
private JButton myTryAgainButton;
private JButton myChangeProxySettingsButton;
private JPanel myEditorPanel;
private JTextField myUrlTextField;
private JPanel myToolbarPanel;
private final Project myProject;
private final HttpVirtualFile myVirtualFile;
private final MergingUpdateQueue myProgressUpdatesQueue;
private final MyDownloadingListener myDownloadingListener;
private final EventDispatcher<PropertyChangeListener> myDispatcher = EventDispatcher.create(PropertyChangeListener.class);
private @Nullable TextEditor myFileEditor;
public DownloadRemoteFilePanel(final Project project, final HttpVirtualFile virtualFile) {
public RemoteFilePanel(final Project project, final HttpVirtualFile virtualFile) {
myProject = project;
myVirtualFile = virtualFile;
myErrorLabel.setIcon(ERROR_ICON);
myUrlTextField.setText(virtualFile.getUrl());
myProgressUpdatesQueue = new MergingUpdateQueue("downloading progress updates", 300, false, myMainPanel);
initToolbar(project);
final RemoteFileInfo remoteFileInfo = virtualFile.getFileInfo();
myDownloadingListener = new MyDownloadingListener();
@@ -84,17 +100,27 @@ public class DownloadRemoteFilePanel {
});
showCard(DOWNLOADING_CARD);
remoteFileInfo.startDownloading();
if (remoteFileInfo.isDownloaded()) {
if (remoteFileInfo.getState() == RemoteFileState.DOWNLOADED) {
switchEditor();
}
else {
String errorMessage = remoteFileInfo.getErrorMessage();
if (errorMessage != null) {
myDownloadingListener.errorOccured(errorMessage);
myDownloadingListener.errorOccurred(errorMessage);
}
}
}
private void initToolbar(Project project) {
DefaultActionGroup group = new DefaultActionGroup();
group.add(new RefreshRemoteFileAction(myVirtualFile));
for (RemoteFileEditorActionProvider actionProvider : RemoteFileEditorActionProvider.EP_NAME.getExtensions()) {
group.addAll(actionProvider.createToolbarActions(project, myVirtualFile));
}
final ActionToolbar actionToolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, group, true);
myToolbarPanel.add(actionToolbar.getComponent(), BorderLayout.CENTER);
}
private void showCard(final String name) {
((CardLayout)myContentPanel.getLayout()).show(myContentPanel, name);
}
@@ -103,34 +129,58 @@ public class DownloadRemoteFilePanel {
LOG.debug("Switching editor...");
ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {
final FileEditorManager fileEditorManager = FileEditorManager.getInstance(myProject);
fileEditorManager.closeFile(myVirtualFile);
ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {
//todo[nik] use ActionCallback
fileEditorManager.openFile(myVirtualFile, true);
LOG.debug("Editor for downloaded file opened.");
}
});
final TextEditor textEditor = (TextEditor)TextEditorProvider.getInstance().createEditor(myProject, myVirtualFile);
textEditor.addPropertyChangeListener(RemoteFilePanel.this);
myEditorPanel.removeAll();
myEditorPanel.add(textEditor.getComponent(), BorderLayout.CENTER);
myFileEditor = textEditor;
showCard(EDITOR_CARD);
LOG.debug("Editor for downloaded file opened.");
}
});
}
@Nullable
public TextEditor getFileEditor() {
return myFileEditor;
}
public JPanel getMainPanel() {
return myMainPanel;
}
public void showNotify() {
public void selectNotify() {
myProgressUpdatesQueue.showNotify();
if (myFileEditor != null) {
myFileEditor.selectNotify();
}
}
public void hideNotify() {
public void deselectNotify() {
myProgressUpdatesQueue.hideNotify();
if (myFileEditor != null) {
myFileEditor.deselectNotify();
}
}
public void dispose() {
myVirtualFile.getFileInfo().removeDownloadingListener(myDownloadingListener);
myProgressUpdatesQueue.dispose();
if (myFileEditor != null) {
Disposer.dispose(myFileEditor);
}
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
myDispatcher.addListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
myDispatcher.removeListener(listener);
}
public void propertyChange(PropertyChangeEvent evt) {
myDispatcher.getMulticaster().propertyChange(evt);
}
private class MyDownloadingListener implements FileDownloadingListener {
@@ -138,9 +188,35 @@ public class DownloadRemoteFilePanel {
switchEditor();
}
public void errorOccured(@NotNull final String errorMessage) {
myErrorLabel.setText(errorMessage);
showCard(ERROR_CARD);
public void downloadingCancelled() {
ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {
if (myFileEditor != null) {
showCard(EDITOR_CARD);
}
else {
myErrorLabel.setText("Downloading cancelled");
showCard(ERROR_CARD);
}
}
});
}
public void downloadingStarted() {
ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {
showCard(DOWNLOADING_CARD);
}
});
}
public void errorOccurred(@NotNull final String errorMessage) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {
myErrorLabel.setText(errorMessage);
showCard(ERROR_CARD);
}
});
}
public void progressMessageChanged(final boolean indeterminate, @NotNull final String message) {
@@ -81,7 +81,6 @@ public class DefaultRemoteContentProvider extends RemoteContentProvider {
int count = 0;
while ((len = input.read(buf)) > 0) {
if (callback.isCancelled()) {
callback.errorOccured(VfsBundle.message("downloading.cancelled.message"));
return;
}
count += len;
@@ -97,7 +96,7 @@ public class DefaultRemoteContentProvider extends RemoteContentProvider {
}
catch (IOException e) {
LOG.info(e);
callback.errorOccured(VfsBundle.message("cannot.load.remote.file", url, e.getMessage()));
callback.errorOccurred(VfsBundle.message("cannot.load.remote.file", url, e.getMessage()), false);
}
finally {
if (input != null) {
@@ -25,7 +25,13 @@ public class FileDownloadingAdapter implements FileDownloadingListener {
public void fileDownloaded(final VirtualFile localFile) {
}
public void errorOccured(@NotNull final String errorMessage) {
public void errorOccurred(@NotNull final String errorMessage) {
}
public void downloadingStarted() {
}
public void downloadingCancelled() {
}
public void progressMessageChanged(final boolean indeterminate, @NotNull final String message) {
@@ -25,7 +25,11 @@ public interface FileDownloadingListener {
void fileDownloaded(final VirtualFile localFile);
void errorOccured(@NotNull String errorMessage);
void errorOccurred(@NotNull String errorMessage);
void downloadingStarted();
void downloadingCancelled();
void progressMessageChanged(final boolean indeterminate, @NotNull String message);
@@ -42,6 +42,10 @@ public class HttpFileSystemImpl extends HttpFileSystem {
myDefaultRemoteContentProvider = new DefaultRemoteContentProvider();
}
public static HttpFileSystemImpl getInstanceImpl() {
return (HttpFileSystemImpl)getInstance();
}
public VirtualFile findFileByPath(@NotNull String path) {
return findFileByPath(path, false);
}
@@ -49,8 +53,7 @@ public class HttpFileSystemImpl extends HttpFileSystem {
public VirtualFile findFileByPath(@NotNull String path, boolean isDirectory) {
try {
String url = VirtualFileManager.constructUrl(PROTOCOL, path);
RemoteContentProvider provider = findContentProvider(url);
return myRemoteFileManager.getOrCreateFile(url, path, isDirectory, provider);
return myRemoteFileManager.getOrCreateFile(url, path, isDirectory);
}
catch (IOException e) {
return null;
@@ -58,7 +61,7 @@ public class HttpFileSystemImpl extends HttpFileSystem {
}
@NotNull
private RemoteContentProvider findContentProvider(final @NotNull String url) {
public RemoteContentProvider findContentProvider(final @NotNull String url) {
for (RemoteContentProvider provider : myProviders) {
if (provider.canProvideContent(url)) {
return provider;
@@ -68,7 +71,7 @@ public class HttpFileSystemImpl extends HttpFileSystem {
}
public boolean isFileDownloaded(@NotNull final VirtualFile file) {
return file instanceof HttpVirtualFile && ((HttpVirtualFile)file).getFileInfo().isDownloaded();
return file instanceof HttpVirtualFile && ((HttpVirtualFile)file).getFileInfo().getState() == RemoteFileState.DOWNLOADED;
}
public void addRemoteContentProvider(@NotNull final RemoteContentProvider provider, @NotNull Disposable parentDisposable) {
@@ -37,7 +37,7 @@ public abstract class RemoteContentProvider {
public interface DownloadingCallback {
void finished(@Nullable FileType fileType);
void errorOccured(@NotNull String errorMessage);
void errorOccurred(@NotNull String errorMessage, boolean cancelled);
void setProgressText(@NotNull String text, boolean indeterminate);
@@ -22,6 +22,7 @@ import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsBundle;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull;
@@ -30,6 +31,7 @@ import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* @author nik
@@ -39,18 +41,18 @@ public class RemoteFileInfo implements RemoteContentProvider.DownloadingCallback
private final Object myLock = new Object();
private final String myUrl;
private final RemoteFileManager myManager;
private final RemoteContentProvider myContentProvider;
private @Nullable RemoteContentProvider myContentProvider;
private File myLocalFile;
private VirtualFile myLocalVirtualFile;
private boolean myDownloaded;
private VirtualFile myPrevLocalFile;
private RemoteFileState myState = RemoteFileState.DOWNLOADING_NOT_STARTED;
private String myErrorMessage;
private volatile boolean myCancelled;
private final AtomicBoolean myCancelled = new AtomicBoolean();
private final List<FileDownloadingListener> myListeners = new SmartList<FileDownloadingListener>();
public RemoteFileInfo(final @NotNull String url, final @NotNull RemoteFileManager manager, final @NotNull RemoteContentProvider provider) {
public RemoteFileInfo(final @NotNull String url, final @NotNull RemoteFileManager manager) {
myUrl = url;
myManager = manager;
myContentProvider = provider;
}
public void addDownloadingListener(@NotNull FileDownloadingListener listener) {
@@ -72,8 +74,9 @@ public class RemoteFileInfo implements RemoteContentProvider.DownloadingCallback
public void restartDownloading() {
synchronized (myLock) {
myErrorMessage = null;
myPrevLocalFile = myLocalVirtualFile;
myLocalVirtualFile = null;
myDownloaded = false;
myState = RemoteFileState.DOWNLOADING_NOT_STARTED;
myLocalFile = null;
startDownloading();
}
@@ -83,20 +86,13 @@ public class RemoteFileInfo implements RemoteContentProvider.DownloadingCallback
LOG.debug("Downloading requested");
File localFile;
FileDownloadingListener[] listeners;
synchronized (myLock) {
if (myDownloaded) {
LOG.debug("File already downloaded: " + myLocalVirtualFile);
return;
}
if (myErrorMessage != null) {
LOG.debug("Error occured: " + myErrorMessage);
return;
}
if (myLocalFile != null) {
LOG.debug("Downloading in progress");
if (myState != RemoteFileState.DOWNLOADING_NOT_STARTED) {
LOG.debug("File already downloaded: file = " + myLocalVirtualFile + ", state = " + myState);
return;
}
myState = RemoteFileState.DOWNLOADING_IN_PROGRESS;
try {
myLocalFile = myManager.getStorage().createLocalFile(myUrl);
@@ -104,13 +100,20 @@ public class RemoteFileInfo implements RemoteContentProvider.DownloadingCallback
}
catch (IOException e) {
LOG.info(e);
errorOccured(VfsBundle.message("cannot.create.local.file", e.getMessage()));
errorOccurred(VfsBundle.message("cannot.create.local.file", e.getMessage()), false);
return;
}
myCancelled = false;
myCancelled.set(false);
localFile = myLocalFile;
listeners = myListeners.toArray(new FileDownloadingListener[myListeners.size()]);
}
for (FileDownloadingListener listener : listeners) {
listener.downloadingStarted();
}
if (myContentProvider == null) {
myContentProvider = HttpFileSystemImpl.getInstanceImpl().findContentProvider(myUrl);
}
myContentProvider.saveContent(myUrl, localFile, this);
}
@@ -152,7 +155,8 @@ public class RemoteFileInfo implements RemoteContentProvider.DownloadingCallback
FileDownloadingListener[] listeners;
synchronized (myLock) {
myLocalVirtualFile = localFile;
myDownloaded = true;
myPrevLocalFile = null;
myState = RemoteFileState.DOWNLOADED;
myErrorMessage = null;
listeners = myListeners.toArray(new FileDownloadingListener[myListeners.size()]);
}
@@ -162,7 +166,7 @@ public class RemoteFileInfo implements RemoteContentProvider.DownloadingCallback
}
public boolean isCancelled() {
return myCancelled;
return myCancelled.get();
}
public String getErrorMessage() {
@@ -171,16 +175,20 @@ public class RemoteFileInfo implements RemoteContentProvider.DownloadingCallback
}
}
public void errorOccured(@NotNull final String errorMessage) {
public void errorOccurred(@NotNull final String errorMessage, boolean cancelled) {
LOG.debug("Error: " + errorMessage);
FileDownloadingListener[] listeners;
synchronized (myLock) {
myLocalVirtualFile = null;
myPrevLocalFile = null;
myState = RemoteFileState.ERROR_OCCURRED;
myErrorMessage = errorMessage;
listeners = myListeners.toArray(new FileDownloadingListener[myListeners.size()]);
}
for (FileDownloadingListener listener : listeners) {
listener.errorOccured(errorMessage);
if (!cancelled) {
listener.errorOccurred(errorMessage);
}
}
}
@@ -210,15 +218,29 @@ public class RemoteFileInfo implements RemoteContentProvider.DownloadingCallback
}
}
public boolean isDownloaded() {
public RemoteFileState getState() {
synchronized (myLock) {
return myDownloaded;
return myState;
}
}
public void cancelDownloading() {
FileDownloadingListener[] listeners;
synchronized (myLock) {
myCancelled = true;
myCancelled.set(true);
if (myPrevLocalFile != null) {
myLocalVirtualFile = myPrevLocalFile;
myLocalFile = VfsUtil.virtualToIoFile(myLocalVirtualFile);
myState = RemoteFileState.DOWNLOADED;
myErrorMessage = null;
}
else {
myState = RemoteFileState.ERROR_OCCURRED;
}
listeners = myListeners.toArray(new FileDownloadingListener[myListeners.size()]);
}
for (FileDownloadingListener listener : listeners) {
listener.downloadingCancelled();
}
}
@@ -227,7 +249,9 @@ public class RemoteFileInfo implements RemoteContentProvider.DownloadingCallback
synchronized (myLock) {
localVirtualFile = myLocalVirtualFile;
}
if ((localVirtualFile == null || !myContentProvider.isUpToDate(myUrl, localVirtualFile)) && myContentProvider.canProvideContent(myUrl)) {
final RemoteContentProvider contentProvider = HttpFileSystemImpl.getInstanceImpl().findContentProvider(myUrl);
if ((localVirtualFile == null || !contentProvider.equals(myContentProvider) || !contentProvider.isUpToDate(myUrl, localVirtualFile))) {
myContentProvider = contentProvider;
addDownloadingListener(new MyRefreshingDownloadingListener(postRunnable));
restartDownloading();
}
@@ -240,6 +264,14 @@ public class RemoteFileInfo implements RemoteContentProvider.DownloadingCallback
myPostRunnable = postRunnable;
}
@Override
public void downloadingCancelled() {
removeDownloadingListener(this);
if (myPostRunnable != null) {
myPostRunnable.run();
}
}
@Override
public void fileDownloaded(final VirtualFile localFile) {
removeDownloadingListener(this);
@@ -249,12 +281,11 @@ public class RemoteFileInfo implements RemoteContentProvider.DownloadingCallback
}
@Override
public void errorOccured(@NotNull final String errorMessage) {
public void errorOccurred(@NotNull final String errorMessage) {
removeDownloadingListener(this);
if (myPostRunnable != null) {
myPostRunnable.run();
}
}
}
}
@@ -15,9 +15,8 @@
*/
package com.intellij.openapi.vfs.impl.http;
import com.intellij.openapi.util.Trinity;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.diagnostic.Logger;
import gnu.trove.THashMap;
import org.jetbrains.annotations.NotNull;
@@ -28,26 +27,24 @@ import java.util.Map;
* @author nik
*/
public class RemoteFileManager {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vfs.impl.http.RemoteFileManager");
private final LocalFileStorage myStorage;
private final HttpFileSystemImpl myHttpFileSystem;
private final Map<Trinity<Boolean, RemoteContentProvider, String>, VirtualFileImpl> myRemoteFiles;
private final Map<Pair<Boolean, String>, VirtualFileImpl> myRemoteFiles;
public RemoteFileManager(final HttpFileSystemImpl httpFileSystem) {
myHttpFileSystem = httpFileSystem;
myStorage = new LocalFileStorage();
myRemoteFiles = new THashMap<Trinity<Boolean, RemoteContentProvider, String>, VirtualFileImpl>();
myRemoteFiles = new THashMap<Pair<Boolean, String>, VirtualFileImpl>();
}
public synchronized VirtualFileImpl getOrCreateFile(final @NotNull String url, final @NotNull String path, final boolean directory,
final @NotNull RemoteContentProvider provider) throws IOException {
Trinity<Boolean, RemoteContentProvider, String> key = Trinity.create(directory, provider, url);
public synchronized VirtualFileImpl getOrCreateFile(final @NotNull String url, final @NotNull String path, final boolean directory) throws IOException {
Pair<Boolean, String> key = Pair.create(directory, url);
VirtualFileImpl file = myRemoteFiles.get(key);
if (file == null) {
if (!directory) {
RemoteFileInfo fileInfo = new RemoteFileInfo(url, this, provider);
RemoteFileInfo fileInfo = new RemoteFileInfo(url, this);
file = new VirtualFileImpl(myHttpFileSystem, path, fileInfo);
fileInfo.addDownloadingListener(new MyDownloadingListener(myHttpFileSystem, file));
}
@@ -0,0 +1,23 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* 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.intellij.openapi.vfs.impl.http;
/**
* @author nik
*/
public enum RemoteFileState {
DOWNLOADING_NOT_STARTED, DOWNLOADING_IN_PROGRESS, DOWNLOADED, ERROR_OCCURRED
}
@@ -491,6 +491,8 @@
<projectService serviceInterface="com.intellij.ide.bookmarks.BookmarkManager"
serviceImplementation="com.intellij.ide.bookmarks.BookmarkManager"/>
<http.fileEditorActionProvider implementation="com.intellij.openapi.fileEditor.impl.http.LangRemoteFileEditorActionProvider"/>
<toolWindow id="Project" anchor="left" icon="/general/toolWindowProject.png"
factoryClass="com.intellij.ide.projectView.impl.ProjectViewToolWindowFactory"/>
<toolWindow id="TODO" anchor="bottom" icon="/general/toolWindowTodo.png"
@@ -72,6 +72,9 @@
<extensionPoint name="editorTabTitleProvider" interface="com.intellij.openapi.fileEditor.impl.EditorTabTitleProvider"/>
<extensionPoint name="editorTabColorProvider" interface="com.intellij.openapi.fileEditor.impl.EditorTabColorProvider"/>
<extensionPoint name="http.localFileFinder" interface="com.intellij.openapi.fileEditor.impl.http.LocalFileFinder"/>
<extensionPoint name="http.fileEditorActionProvider" interface="com.intellij.openapi.fileEditor.impl.http.RemoteFileEditorActionProvider"/>
<extensionPoint name="componentConfigurationMerger" interface="com.intellij.openapi.components.XmlConfigurationMerger"/>
<extensionPoint name="filetype.decompiler" beanClass="com.intellij.openapi.fileTypes.FileTypeExtensionPoint"/>