/* * 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 org.community.intellij.plugins.communitycase.update; import com.intellij.openapi.application.ApplicationNamesInfo; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.vcsUtil.VcsUtil; import org.community.intellij.plugins.communitycase.Util; import org.community.intellij.plugins.communitycase.commands.Command; import org.community.intellij.plugins.communitycase.commands.SimpleHandler; import org.community.intellij.plugins.communitycase.commands.StringScanner; import org.community.intellij.plugins.communitycase.i18n.Bundle; import org.community.intellij.plugins.communitycase.rollback.RollbackEnvironment; import org.community.intellij.plugins.communitycase.ui.UiUtil; import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; /** * The dialog that displays locally modified files during update process */ public class UpdateLocallyModifiedDialog extends DialogWrapper { /** * The rescan button */ private JButton myRescanButton; /** * The list of files to undoCheckout */ private JList myFilesList; private JLabel myDescriptionLabel; /** * The root label */ private JLabel myRoot; /** * The root panel */ private JPanel myRootPanel; /** * The collection with locally modified files */ private final List<String> myLocallyModifiedFiles; /** * The constructor * * @param project the current project * @param root the vcs root * @param locallyModifiedFiles the collection of locally modified files to use */ protected UpdateLocallyModifiedDialog(final Project project, final VirtualFile root, List<String> locallyModifiedFiles) { super(project, true); myLocallyModifiedFiles = locallyModifiedFiles; setTitle(Bundle.getString("update.locally.modified.title")); myRoot.setText(root.getPresentableUrl()); myFilesList.setModel(new DefaultListModel()); setOKButtonText(Bundle.getString("update.locally.modified.revert")); syncListModel(); myRescanButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { myLocallyModifiedFiles.clear(); try { scanFiles(project, root, myLocallyModifiedFiles); } catch (VcsException ex) { UiUtil.showOperationError(project, ex, "Checking for locally modified files"); } } }); myDescriptionLabel .setText(Bundle.message("update.locally.modified.message", ApplicationNamesInfo.getInstance().getFullProductName())); init(); } /** * Refresh list model according to the current content of the collection */ private void syncListModel() { DefaultListModel listModel = (DefaultListModel)myFilesList.getModel(); listModel.removeAllElements(); for (String p : myLocallyModifiedFiles) { listModel.addElement(p); } } /** * {@inheritDoc} */ @Override protected JComponent createCenterPanel() { return myRootPanel; } /** * {@inheritDoc} */ @Override protected String getDimensionServiceKey() { return getClass().getName(); } /** * Scan working tree and detect locally modified files * * @param project the project to scan * @param root the root to scan * @param files the collection with files * @throws VcsException if there problem with running or working tree is dirty in unsupported way */ private static void scanFiles(Project project, VirtualFile root, List<String> files) throws VcsException { String rootPath = root.getPath(); SimpleHandler h = new SimpleHandler(project, root, Command.DIFF); h.addParameters("--name-status"); h.setRemote(true); h.setStdoutSuppressed(true); StringScanner s = new StringScanner(h.run()); while (s.hasMoreData()) { if (s.isEol()) { s.line(); continue; } if (s.tryConsume("M\t")) { String path = rootPath + "/" + Util.unescapePath(s.line()); files.add(path); } else { throw new VcsException("Working tree is dirty in unsupported way: " + s.line()); } } } /** * Show the dialog if needed * * @param project the project * @param root the vcs root * @return true if showing is not needed or operation completed successfully */ public static boolean showIfNeeded(final Project project, final VirtualFile root) { final ArrayList<String> files = new ArrayList<String>(); try { scanFiles(project, root, files); final AtomicBoolean rc = new AtomicBoolean(true); if (!files.isEmpty()) { com.intellij.util.ui.UIUtil.invokeAndWaitIfNeeded(new Runnable() { public void run() { UpdateLocallyModifiedDialog d = new UpdateLocallyModifiedDialog(project, root, files); d.show(); rc.set(d.isOK()); } }); if (rc.get()) { if (!files.isEmpty()) { revertFiles(project, root, files); } } } return rc.get(); } catch (final VcsException e) { com.intellij.util.ui.UIUtil.invokeAndWaitIfNeeded(new Runnable() { public void run() { UiUtil.showOperationError(project, e, "Checking for locally modified files"); } }); return false; } } /** * Revert files from the list * * @param project the project * @param root the vcs root * @param files the files to undoCheckout */ private static void revertFiles(Project project, VirtualFile root, ArrayList<String> files) throws VcsException { // TODO consider deleted files RollbackEnvironment rollback = RollbackEnvironment.getInstance(project); ArrayList<FilePath> list = new ArrayList<FilePath>(files.size()); for (String p : files) { list.add(VcsUtil.getFilePath(p)); } rollback.undoCheckout(root, list); } }