2 * Copyright 2000-2015 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.diff.impl;
18 import com.intellij.codeInsight.hint.HintManager;
19 import com.intellij.codeInsight.hint.HintManagerImpl;
20 import com.intellij.codeInsight.hint.HintUtil;
21 import com.intellij.diff.*;
22 import com.intellij.diff.FrameDiffTool.DiffViewer;
23 import com.intellij.diff.actions.impl.*;
24 import com.intellij.diff.impl.DiffSettingsHolder.DiffSettings;
25 import com.intellij.diff.requests.DiffRequest;
26 import com.intellij.diff.requests.ErrorDiffRequest;
27 import com.intellij.diff.requests.LoadingDiffRequest;
28 import com.intellij.diff.requests.MessageDiffRequest;
29 import com.intellij.diff.tools.ErrorDiffTool;
30 import com.intellij.diff.tools.external.ExternalDiffTool;
31 import com.intellij.diff.tools.util.DiffDataKeys;
32 import com.intellij.diff.tools.util.PrevNextDifferenceIterable;
33 import com.intellij.diff.util.DiffUserDataKeys;
34 import com.intellij.diff.util.DiffUserDataKeysEx;
35 import com.intellij.diff.util.DiffUserDataKeysEx.ScrollToPolicy;
36 import com.intellij.diff.util.DiffUtil;
37 import com.intellij.diff.util.LineRange;
38 import com.intellij.ide.DataManager;
39 import com.intellij.ide.impl.DataManagerImpl;
40 import com.intellij.openapi.Disposable;
41 import com.intellij.openapi.actionSystem.*;
42 import com.intellij.openapi.actionSystem.ex.ComboBoxAction;
43 import com.intellij.openapi.diagnostic.Logger;
44 import com.intellij.openapi.editor.Editor;
45 import com.intellij.openapi.editor.LogicalPosition;
46 import com.intellij.openapi.project.DumbAware;
47 import com.intellij.openapi.project.DumbAwareAction;
48 import com.intellij.openapi.project.Project;
49 import com.intellij.openapi.ui.Messages;
50 import com.intellij.openapi.ui.popup.Balloon;
51 import com.intellij.openapi.ui.popup.JBPopupFactory;
52 import com.intellij.openapi.ui.popup.ListPopup;
53 import com.intellij.openapi.util.*;
54 import com.intellij.openapi.wm.IdeFocusManager;
55 import com.intellij.openapi.wm.ex.IdeFocusTraversalPolicy;
56 import com.intellij.ui.ColorUtil;
57 import com.intellij.ui.HintHint;
58 import com.intellij.ui.JBColor;
59 import com.intellij.ui.LightweightHint;
60 import com.intellij.ui.components.panels.Wrapper;
61 import com.intellij.util.containers.ContainerUtil;
62 import com.intellij.util.ui.JBUI;
63 import com.intellij.util.ui.UIUtil;
64 import org.jetbrains.annotations.CalledInAwt;
65 import org.jetbrains.annotations.NonNls;
66 import org.jetbrains.annotations.NotNull;
67 import org.jetbrains.annotations.Nullable;
71 import java.util.ArrayList;
72 import java.util.LinkedList;
73 import java.util.List;
75 @SuppressWarnings("InnerClassMayBeStatic")
76 public abstract class DiffRequestProcessor implements Disposable {
77 private static final Logger LOG = Logger.getInstance(DiffRequestProcessor.class);
79 private boolean myDisposed;
81 @Nullable private final Project myProject;
82 @NotNull private final DiffContext myContext;
84 @NotNull private final DiffSettings mySettings;
85 @NotNull private final List<DiffTool> myAvailableTools;
86 @NotNull private final LinkedList<DiffTool> myToolOrder;
88 @NotNull private final OpenInEditorAction myOpenInEditorAction;
89 @Nullable private DefaultActionGroup myPopupActionGroup;
91 @NotNull private final JPanel myPanel;
92 @NotNull private final MyPanel myMainPanel;
93 @NotNull private final Wrapper myContentPanel;
94 @NotNull private final Wrapper myToolbarPanel; // TODO: allow to call 'updateToolbar' from Viewer ?
95 @NotNull private final Wrapper myToolbarStatusPanel;
97 @NotNull private DiffRequest myActiveRequest;
99 @NotNull private ViewerState myState;
101 public DiffRequestProcessor(@Nullable Project project) {
102 this(project, new UserDataHolderBase());
105 public DiffRequestProcessor(@Nullable Project project, @NotNull String place) {
106 this(project, DiffUtil.createUserDataHolder(DiffUserDataKeysEx.PLACE, place));
109 public DiffRequestProcessor(@Nullable Project project, @NotNull UserDataHolder context) {
112 myContext = new MyDiffContext(context);
113 myActiveRequest = new LoadingDiffRequest();
115 mySettings = DiffSettingsHolder.getInstance().getSettings(myContext.getUserData(DiffUserDataKeysEx.PLACE));
117 myAvailableTools = DiffManagerEx.getInstance().getDiffTools();
118 myToolOrder = new LinkedList<DiffTool>(getToolOrderFromSettings(myAvailableTools));
122 myPanel = new JPanel(new BorderLayout());
123 myMainPanel = new MyPanel();
124 myContentPanel = new Wrapper();
125 myToolbarPanel = new Wrapper();
126 myToolbarPanel.setFocusable(true);
127 myToolbarStatusPanel = new Wrapper();
129 myPanel.add(myMainPanel, BorderLayout.CENTER);
131 JPanel topPanel = new JPanel(new BorderLayout());
132 topPanel.add(myToolbarPanel, BorderLayout.CENTER);
133 topPanel.add(myToolbarStatusPanel, BorderLayout.EAST);
136 myMainPanel.add(topPanel, BorderLayout.NORTH);
137 myMainPanel.add(myContentPanel, BorderLayout.CENTER);
139 myMainPanel.setFocusTraversalPolicyProvider(true);
140 myMainPanel.setFocusTraversalPolicy(new MyFocusTraversalPolicy());
142 JComponent bottomPanel = myContext.getUserData(DiffUserDataKeysEx.BOTTOM_PANEL);
143 if (bottomPanel != null) myMainPanel.add(bottomPanel, BorderLayout.SOUTH);
144 if (bottomPanel instanceof Disposable) Disposer.register(this, (Disposable)bottomPanel);
146 myState = new EmptyState();
147 myContentPanel.setContent(DiffUtil.createMessagePanel(((LoadingDiffRequest)myActiveRequest).getMessage()));
149 myOpenInEditorAction = new OpenInEditorAction(new Runnable() {
162 protected void reloadRequest() {
167 public void updateRequest() {
168 updateRequest(false);
172 public void updateRequest(boolean force) {
173 updateRequest(force, null);
177 public abstract void updateRequest(boolean force, @Nullable ScrollToPolicy scrollToChangePolicy);
180 private FrameDiffTool getFittedTool() {
181 List<FrameDiffTool> tools = new ArrayList<FrameDiffTool>();
182 for (DiffTool tool : myToolOrder) {
184 if (tool instanceof FrameDiffTool && tool.canShow(myContext, myActiveRequest)) {
185 tools.add((FrameDiffTool)tool);
188 catch (Throwable e) {
193 tools = DiffUtil.filterSuppressedTools(tools);
195 return tools.isEmpty() ? ErrorDiffTool.INSTANCE : tools.get(0);
199 private List<FrameDiffTool> getAvailableFittedTools() {
200 List<FrameDiffTool> tools = new ArrayList<FrameDiffTool>();
201 for (DiffTool tool : myAvailableTools) {
203 if (tool instanceof FrameDiffTool && tool.canShow(myContext, myActiveRequest)) {
204 tools.add((FrameDiffTool)tool);
207 catch (Throwable e) {
212 return DiffUtil.filterSuppressedTools(tools);
215 private void moveToolOnTop(@NotNull DiffTool tool) {
216 myToolOrder.remove(tool);
218 FrameDiffTool toolToReplace = getFittedTool();
221 for (index = 0; index < myToolOrder.size(); index++) {
222 if (myToolOrder.get(index) == toolToReplace) break;
224 myToolOrder.add(index, tool);
226 updateToolOrderSettings(myToolOrder);
230 private ViewerState createState() {
231 FrameDiffTool frameTool = getFittedTool();
233 DiffViewer viewer = frameTool.createComponent(myContext, myActiveRequest);
235 for (DiffExtension extension : DiffExtension.EP_NAME.getExtensions()) {
236 extension.onViewerCreated(viewer, myContext, myActiveRequest);
239 DiffViewerWrapper wrapper = myActiveRequest.getUserData(DiffViewerWrapper.KEY);
240 if (wrapper == null) {
241 return new DefaultState(viewer, frameTool);
244 return new WrapperState(viewer, frameTool, wrapper);
252 @Nullable private ApplyData myQueuedApplyRequest;
255 protected void applyRequest(@NotNull DiffRequest request, boolean force, @Nullable ScrollToPolicy scrollToChangePolicy) {
256 myIterationState = IterationState.NONE;
258 force = force || (myQueuedApplyRequest != null && myQueuedApplyRequest.force);
259 myQueuedApplyRequest = new ApplyData(request, force, scrollToChangePolicy);
260 Runnable task = new Runnable() {
263 if (myQueuedApplyRequest == null || myDisposed) return;
264 doApplyRequest(myQueuedApplyRequest.request, myQueuedApplyRequest.force, myQueuedApplyRequest.scrollToChangePolicy);
265 myQueuedApplyRequest = null;
269 IdeFocusManager.getInstance(myProject).doWhenFocusSettlesDown(task);
273 private void doApplyRequest(@NotNull DiffRequest request, boolean force, @Nullable ScrollToPolicy scrollToChangePolicy) {
274 if (!force && request == myActiveRequest) return;
276 request.putUserData(DiffUserDataKeysEx.SCROLL_TO_CHANGE, scrollToChangePolicy);
278 boolean hadFocus = isFocused();
281 myToolbarStatusPanel.setContent(null);
282 myToolbarPanel.setContent(null);
283 myContentPanel.setContent(null);
284 myMainPanel.putClientProperty(AnAction.ourClientProperty, null);
286 myActiveRequest.onAssigned(false);
287 myActiveRequest = request;
288 myActiveRequest.onAssigned(true);
291 myState = createState();
294 catch (Throwable e) {
296 myState = new ErrorState(new ErrorDiffRequest("Error: can't show diff"), getFittedTool());
300 if (hadFocus) requestFocusInternal();
303 protected void setWindowTitle(@NotNull String title) {
306 protected void onAfterNavigate() {
310 protected void onDispose() {
314 public <T> T getContextUserData(@NotNull Key<T> key) {
315 return myContext.getUserData(key);
318 public <T> void putContextUserData(@NotNull Key<T> key, @Nullable T value) {
319 myContext.putUserData(key, value);
323 protected List<AnAction> getNavigationActions() {
324 return ContainerUtil.<AnAction>list(
325 new MyPrevDifferenceAction(),
326 new MyNextDifferenceAction(),
327 new MyPrevChangeAction(),
328 new MyNextChangeAction()
336 public boolean isWindowFocused() {
337 Window window = SwingUtilities.getWindowAncestor(myPanel);
338 return window != null && window.isFocused();
341 public boolean isFocused() {
342 return DiffUtil.isFocusedComponent(myProject, myPanel);
345 public void requestFocus() {
346 DiffUtil.requestFocus(myProject, getPreferredFocusedComponent());
349 protected void requestFocusInternal() {
350 JComponent component = getPreferredFocusedComponent();
351 if (component != null) component.requestFocus();
355 protected List<DiffTool> getToolOrderFromSettings(@NotNull List<DiffTool> availableTools) {
356 List<DiffTool> result = new ArrayList<DiffTool>();
357 List<String> savedOrder = getSettings().getDiffToolsOrder();
359 for (final String clazz : savedOrder) {
360 DiffTool tool = ContainerUtil.find(availableTools, new Condition<DiffTool>() {
362 public boolean value(DiffTool tool) {
363 return tool.getClass().getCanonicalName().equals(clazz);
366 if (tool != null) result.add(tool);
369 for (DiffTool tool : availableTools) {
370 if (!result.contains(tool)) result.add(tool);
376 protected void updateToolOrderSettings(@NotNull List<DiffTool> toolOrder) {
377 List<String> savedOrder = new ArrayList<String>();
378 for (DiffTool tool : toolOrder) {
379 savedOrder.add(tool.getClass().getCanonicalName());
381 getSettings().setDiffToolsOrder(savedOrder);
385 public void dispose() {
386 if (myDisposed) return;
387 UIUtil.invokeLaterIfNeeded(new Runnable() {
390 if (myDisposed) return;
396 myToolbarStatusPanel.setContent(null);
397 myToolbarPanel.setContent(null);
398 myContentPanel.setContent(null);
400 myActiveRequest.onAssigned(false);
406 protected DefaultActionGroup collectToolbarActions(@Nullable List<AnAction> viewerActions) {
407 DefaultActionGroup group = new DefaultActionGroup();
409 List<AnAction> navigationActions = new ArrayList<AnAction>();
410 navigationActions.addAll(getNavigationActions());
411 navigationActions.add(myOpenInEditorAction);
412 navigationActions.add(new MyChangeDiffToolAction());
413 DiffUtil.addActionBlock(group,
416 DiffUtil.addActionBlock(group, viewerActions);
418 List<AnAction> requestContextActions = myActiveRequest.getUserData(DiffUserDataKeys.CONTEXT_ACTIONS);
419 DiffUtil.addActionBlock(group, requestContextActions);
421 List<AnAction> contextActions = myContext.getUserData(DiffUserDataKeys.CONTEXT_ACTIONS);
422 DiffUtil.addActionBlock(group, contextActions);
424 DiffUtil.addActionBlock(group,
425 new ShowInExternalToolAction(),
426 new ShowOldDiffAction(),
427 ActionManager.getInstance().getAction(IdeActions.ACTION_CONTEXT_HELP));
433 protected DefaultActionGroup collectPopupActions(@Nullable List<AnAction> viewerActions) {
434 DefaultActionGroup group = new DefaultActionGroup();
436 List<AnAction> selectToolActions = new ArrayList<AnAction>();
437 for (DiffTool tool : getAvailableFittedTools()) {
438 if (tool == myState.getActiveTool()) continue;
439 selectToolActions.add(new DiffToolToggleAction(tool));
441 DiffUtil.addActionBlock(group, selectToolActions);
443 DiffUtil.addActionBlock(group, viewerActions);
448 protected void buildToolbar(@Nullable List<AnAction> viewerActions) {
449 ActionGroup group = collectToolbarActions(viewerActions);
450 ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.DIFF_TOOLBAR, group, true);
452 DataManager.registerDataProvider(toolbar.getComponent(), myMainPanel);
453 toolbar.setTargetComponent(toolbar.getComponent());
455 myToolbarPanel.setContent(toolbar.getComponent());
456 for (AnAction action : group.getChildren(null)) {
457 DiffUtil.registerAction(action, myMainPanel);
461 protected void buildActionPopup(@Nullable List<AnAction> viewerActions) {
462 ShowActionGroupPopupAction action = new ShowActionGroupPopupAction();
463 DiffUtil.registerAction(action, myMainPanel);
465 myPopupActionGroup = collectPopupActions(viewerActions);
468 private void setTitle(@Nullable String title) {
469 if (getContextUserData(DiffUserDataKeys.DO_NOT_CHANGE_WINDOW_TITLE) == Boolean.TRUE) return;
470 if (title == null) title = "Diff";
471 setWindowTitle(title);
479 public JComponent getComponent() {
484 public JComponent getPreferredFocusedComponent() {
485 JComponent component = myState.getPreferredFocusedComponent();
486 return component != null ? component : myToolbarPanel.getTargetComponent();
490 public Project getProject() {
495 public DiffContext getContext() {
500 protected DiffSettings getSettings() {
508 private class ShowInExternalToolAction extends DumbAwareAction {
509 public ShowInExternalToolAction() {
510 EmptyAction.setupAction(this, "Diff.ShowInExternalTool", null);
514 public void update(AnActionEvent e) {
515 if (!ExternalDiffTool.isEnabled()) {
516 e.getPresentation().setEnabledAndVisible(false);
519 e.getPresentation().setEnabled(ExternalDiffTool.canShow(myActiveRequest));
520 e.getPresentation().setVisible(true);
524 public void actionPerformed(AnActionEvent e) {
526 ExternalDiffTool.showRequest(e.getProject(), myActiveRequest);
528 catch (Throwable ex) {
529 Messages.showErrorDialog(e.getProject(), ex.getMessage(), "Can't Show Diff In External Tool");
534 private class MyChangeDiffToolAction extends ComboBoxAction implements DumbAware {
535 public MyChangeDiffToolAction() {
536 // TODO: add icons for diff tools, show only icon in toolbar - to reduce jumping on change ?
537 setEnabledInModalContext(true);
541 public void update(AnActionEvent e) {
542 Presentation presentation = e.getPresentation();
544 DiffTool activeTool = myState.getActiveTool();
545 presentation.setText(activeTool.getName());
547 if (activeTool == ErrorDiffTool.INSTANCE) {
548 presentation.setEnabledAndVisible(false);
551 for (DiffTool tool : getAvailableFittedTools()) {
552 if (tool != activeTool) {
553 presentation.setEnabledAndVisible(true);
558 presentation.setEnabledAndVisible(false);
563 protected DefaultActionGroup createPopupActionGroup(JComponent button) {
564 DefaultActionGroup group = new DefaultActionGroup();
565 for (DiffTool tool : getAvailableFittedTools()) {
566 group.add(new DiffToolToggleAction(tool));
573 private class DiffToolToggleAction extends AnAction implements DumbAware {
574 @NotNull private final DiffTool myDiffTool;
576 private DiffToolToggleAction(@NotNull DiffTool tool) {
577 super(tool.getName());
578 setEnabledInModalContext(true);
583 public void actionPerformed(@NotNull AnActionEvent e) {
584 if (myState.getActiveTool() == myDiffTool) return;
586 moveToolOnTop(myDiffTool);
592 private class ShowActionGroupPopupAction extends DumbAwareAction {
593 public ShowActionGroupPopupAction() {
594 EmptyAction.setupAction(this, "Diff.ShowSettingsPopup", null);
598 public void update(AnActionEvent e) {
599 e.getPresentation().setEnabled(myPopupActionGroup != null && myPopupActionGroup.getChildrenCount() > 0);
603 public void actionPerformed(AnActionEvent e) {
604 assert myPopupActionGroup != null;
605 ListPopup popup = JBPopupFactory.getInstance().createActionGroupPopup("Diff Actions", myPopupActionGroup, e.getDataContext(),
606 JBPopupFactory.ActionSelectionAid.SPEEDSEARCH, false);
607 popup.showInCenterOf(myPanel);
615 private enum IterationState {NEXT, PREV, NONE}
617 @NotNull private IterationState myIterationState = IterationState.NONE;
620 protected boolean hasNextChange() {
625 protected boolean hasPrevChange() {
630 protected void goToNextChange(boolean fromDifferences) {
634 protected void goToPrevChange(boolean fromDifferences) {
638 protected boolean isNavigationEnabled() {
642 protected class MyNextDifferenceAction extends NextDifferenceAction {
644 public void update(@NotNull AnActionEvent e) {
645 if (!ActionPlaces.DIFF_TOOLBAR.equals(e.getPlace())) {
646 e.getPresentation().setEnabledAndVisible(true);
650 PrevNextDifferenceIterable iterable = DiffDataKeys.PREV_NEXT_DIFFERENCE_ITERABLE.getData(e.getDataContext());
651 if (iterable != null && iterable.canGoNext()) {
652 e.getPresentation().setEnabled(true);
656 if (getSettings().isGoToNextFileOnNextDifference() && isNavigationEnabled() && hasNextChange()) {
657 e.getPresentation().setEnabled(true);
661 e.getPresentation().setEnabled(false);
665 public void actionPerformed(@NotNull AnActionEvent e) {
666 PrevNextDifferenceIterable iterable = DiffDataKeys.PREV_NEXT_DIFFERENCE_ITERABLE.getData(e.getDataContext());
667 if (iterable != null && iterable.canGoNext()) {
669 myIterationState = IterationState.NONE;
673 if (!isNavigationEnabled() || !hasNextChange() || !getSettings().isGoToNextFileOnNextDifference()) return;
675 if (myIterationState != IterationState.NEXT) {
676 notifyMessage(e, true);
677 myIterationState = IterationState.NEXT;
681 goToNextChange(true);
685 protected class MyPrevDifferenceAction extends PrevDifferenceAction {
687 public void update(@NotNull AnActionEvent e) {
688 if (!ActionPlaces.DIFF_TOOLBAR.equals(e.getPlace())) {
689 e.getPresentation().setEnabledAndVisible(true);
693 PrevNextDifferenceIterable iterable = DiffDataKeys.PREV_NEXT_DIFFERENCE_ITERABLE.getData(e.getDataContext());
694 if (iterable != null && iterable.canGoPrev()) {
695 e.getPresentation().setEnabled(true);
699 if (getSettings().isGoToNextFileOnNextDifference() && isNavigationEnabled() && hasPrevChange()) {
700 e.getPresentation().setEnabled(true);
704 e.getPresentation().setEnabled(false);
708 public void actionPerformed(@NotNull AnActionEvent e) {
709 PrevNextDifferenceIterable iterable = DiffDataKeys.PREV_NEXT_DIFFERENCE_ITERABLE.getData(e.getDataContext());
710 if (iterable != null && iterable.canGoPrev()) {
712 myIterationState = IterationState.NONE;
716 if (!isNavigationEnabled() || !hasPrevChange() || !getSettings().isGoToNextFileOnNextDifference()) return;
718 if (myIterationState != IterationState.PREV) {
719 notifyMessage(e, false);
720 myIterationState = IterationState.PREV;
724 goToPrevChange(true);
728 private void notifyMessage(@NotNull AnActionEvent e, boolean next) {
729 Editor editor = e.getData(DiffDataKeys.CURRENT_EDITOR);
731 // TODO: provide "change" word in chain UserData - for tests/etc
732 StringBuffer message = new StringBuffer(next ? "Press again to go to the next file" : "Press again to go to the previous file")
733 .append("<br>").append("<span style='color:#").append(ColorUtil.toHex(JBColor.gray)).append("'>").append("<small>")
734 .append("You can disable this feature in ").append(DiffUtil.getSettingsConfigurablePath())
735 .append("</small>").append("</span>");
737 final LightweightHint hint = new LightweightHint(HintUtil.createInformationLabel(message.toString()));
738 Point point = new Point(myContentPanel.getWidth() / 2, next ? myContentPanel.getHeight() - JBUI.scale(40) : JBUI.scale(40));
740 if (editor == null) {
741 final Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
742 final HintHint hintHint = createNotifyHint(myContentPanel, point, next);
743 hint.show(myContentPanel, point.x, point.y, owner instanceof JComponent ? (JComponent)owner : null, hintHint);
746 int x = SwingUtilities.convertPoint(myContentPanel, point, editor.getComponent()).x;
748 JComponent header = editor.getHeaderComponent();
749 int shift = editor.getScrollingModel().getVerticalScrollOffset() - (header != null ? header.getHeight() : 0);
751 LogicalPosition position;
752 LineRange changeRange = e.getData(DiffDataKeys.CURRENT_CHANGE_RANGE);
753 if (changeRange == null) {
754 position = new LogicalPosition(editor.getCaretModel().getLogicalPosition().line + (next ? 1 : 0), 0);
757 position = new LogicalPosition(next ? changeRange.end : changeRange.start, 0);
759 int y = editor.logicalPositionToXY(position).y - shift;
761 Point editorPoint = new Point(x, y);
762 final HintHint hintHint = createNotifyHint(editor.getComponent(), editorPoint, !next);
763 HintManagerImpl.getInstanceImpl().showEditorHint(hint, editor, editorPoint, HintManager.HIDE_BY_ANY_KEY |
764 HintManager.HIDE_BY_TEXT_CHANGE |
765 HintManager.HIDE_BY_SCROLLING, 0, false, hintHint);
770 private static HintHint createNotifyHint(@NotNull JComponent component, @NotNull Point point, boolean above) {
771 return new HintHint(component, point)
772 .setPreferredPosition(above ? Balloon.Position.above : Balloon.Position.below)
774 .setFont(UIUtil.getLabelFont().deriveFont(Font.BOLD))
775 .setTextBg(HintUtil.INFORMATION_COLOR)
776 .setShowImmediately(true);
781 protected class MyNextChangeAction extends NextChangeAction {
783 public void update(@NotNull AnActionEvent e) {
784 if (!ActionPlaces.DIFF_TOOLBAR.equals(e.getPlace())) {
785 e.getPresentation().setEnabledAndVisible(true);
789 if (!isNavigationEnabled()) {
790 e.getPresentation().setEnabledAndVisible(false);
794 e.getPresentation().setVisible(true);
795 e.getPresentation().setEnabled(hasNextChange());
799 public void actionPerformed(@NotNull AnActionEvent e) {
800 if (!isNavigationEnabled() || !hasNextChange()) return;
802 goToNextChange(false);
806 protected class MyPrevChangeAction extends PrevChangeAction {
808 public void update(@NotNull AnActionEvent e) {
809 if (!ActionPlaces.DIFF_TOOLBAR.equals(e.getPlace())) {
810 e.getPresentation().setEnabledAndVisible(true);
814 if (!isNavigationEnabled()) {
815 e.getPresentation().setEnabledAndVisible(false);
819 e.getPresentation().setVisible(true);
820 e.getPresentation().setEnabled(hasPrevChange());
824 public void actionPerformed(@NotNull AnActionEvent e) {
825 if (!isNavigationEnabled() || !hasPrevChange()) return;
827 goToPrevChange(false);
835 private class MyPanel extends JPanel implements DataProvider {
837 super(new BorderLayout());
841 public Dimension getPreferredSize() {
842 Dimension windowSize = DiffUtil.getDefaultDiffPanelSize();
843 Dimension size = super.getPreferredSize();
844 return new Dimension(Math.max(windowSize.width, size.width), Math.max(windowSize.height, size.height));
849 public Object getData(@NonNls String dataId) {
852 DataProvider contentProvider = DataManagerImpl.getDataProviderEx(myContentPanel.getTargetComponent());
853 if (contentProvider != null) {
854 data = contentProvider.getData(dataId);
855 if (data != null) return data;
858 if (OpenInEditorAction.KEY.is(dataId)) {
859 return myOpenInEditorAction;
861 else if (DiffDataKeys.DIFF_REQUEST.is(dataId)) {
862 return myActiveRequest;
864 else if (CommonDataKeys.PROJECT.is(dataId)) {
867 else if (PlatformDataKeys.HELP_ID.is(dataId)) {
868 if (myActiveRequest.getUserData(DiffUserDataKeys.HELP_ID) != null) {
869 return myActiveRequest.getUserData(DiffUserDataKeys.HELP_ID);
872 return "reference.dialogs.diff.file";
875 else if (DiffDataKeys.DIFF_CONTEXT.is(dataId)) {
879 data = myState.getData(dataId);
880 if (data != null) return data;
882 DataProvider requestProvider = myActiveRequest.getUserData(DiffUserDataKeys.DATA_PROVIDER);
883 if (requestProvider != null) {
884 data = requestProvider.getData(dataId);
885 if (data != null) return data;
888 DataProvider contextProvider = myContext.getUserData(DiffUserDataKeys.DATA_PROVIDER);
889 if (contextProvider != null) {
890 data = contextProvider.getData(dataId);
891 if (data != null) return data;
897 private class MyFocusTraversalPolicy extends IdeFocusTraversalPolicy {
899 public final Component getDefaultComponentImpl(final Container focusCycleRoot) {
900 JComponent component = DiffRequestProcessor.this.getPreferredFocusedComponent();
901 if (component == null) return null;
902 return IdeFocusTraversalPolicy.getPreferredFocusedComponent(component, this);
906 private class MyDiffContext extends DiffContextEx {
907 @NotNull private final UserDataHolder myContext;
909 public MyDiffContext(@NotNull UserDataHolder context) {
914 public void reopenDiffRequest() {
919 public void reloadDiffRequest() {
925 public Project getProject() {
926 return DiffRequestProcessor.this.getProject();
930 public boolean isFocused() {
931 return DiffRequestProcessor.this.isFocused();
935 public boolean isWindowFocused() {
936 return DiffRequestProcessor.this.isWindowFocused();
940 public void requestFocus() {
941 DiffRequestProcessor.this.requestFocusInternal();
946 public <T> T getUserData(@NotNull Key<T> key) {
947 return myContext.getUserData(key);
951 public <T> void putUserData(@NotNull Key<T> key, @Nullable T value) {
952 myContext.putUserData(key, value);
956 private static class ApplyData {
957 @NotNull private final DiffRequest request;
958 private final boolean force;
959 @Nullable private final ScrollToPolicy scrollToChangePolicy;
961 public ApplyData(@NotNull DiffRequest request, boolean force, @Nullable ScrollToPolicy scrollToChangePolicy) {
962 this.request = request;
964 this.scrollToChangePolicy = scrollToChangePolicy;
972 private interface ViewerState {
980 JComponent getPreferredFocusedComponent();
983 Object getData(@NonNls String dataId);
986 DiffTool getActiveTool();
989 private static class EmptyState implements ViewerState {
995 public void destroy() {
1000 public JComponent getPreferredFocusedComponent() {
1006 public Object getData(@NonNls String dataId) {
1012 public DiffTool getActiveTool() {
1013 return ErrorDiffTool.INSTANCE;
1017 private class ErrorState implements ViewerState {
1018 @Nullable private final DiffTool myDiffTool;
1019 @NotNull private final MessageDiffRequest myRequest;
1021 @NotNull private final DiffViewer myViewer;
1023 public ErrorState(@NotNull MessageDiffRequest request) {
1024 this(request, null);
1027 public ErrorState(@NotNull MessageDiffRequest request, @Nullable DiffTool diffTool) {
1028 myDiffTool = diffTool;
1029 myRequest = request;
1031 myViewer = ErrorDiffTool.INSTANCE.createComponent(myContext, myRequest);
1036 public void init() {
1037 myContentPanel.setContent(myViewer.getComponent());
1039 FrameDiffTool.ToolbarComponents init = myViewer.init();
1040 buildToolbar(init.toolbarActions);
1045 public void destroy() {
1046 Disposer.dispose(myViewer);
1051 public JComponent getPreferredFocusedComponent() {
1057 public Object getData(@NonNls String dataId) {
1063 public DiffTool getActiveTool() {
1064 return myDiffTool != null ? myDiffTool : ErrorDiffTool.INSTANCE;
1068 private class DefaultState implements ViewerState {
1069 @NotNull private final DiffViewer myViewer;
1070 @NotNull private final FrameDiffTool myTool;
1072 public DefaultState(@NotNull DiffViewer viewer, @NotNull FrameDiffTool tool) {
1079 public void init() {
1080 myContentPanel.setContent(myViewer.getComponent());
1081 setTitle(myActiveRequest.getTitle());
1083 FrameDiffTool.ToolbarComponents toolbarComponents = myViewer.init();
1085 buildToolbar(toolbarComponents.toolbarActions);
1086 buildActionPopup(toolbarComponents.popupActions);
1088 myToolbarStatusPanel.setContent(toolbarComponents.statusPanel);
1093 public void destroy() {
1094 Disposer.dispose(myViewer);
1099 public JComponent getPreferredFocusedComponent() {
1100 return myViewer.getPreferredFocusedComponent();
1105 public DiffTool getActiveTool() {
1111 public Object getData(@NonNls String dataId) {
1112 if (DiffDataKeys.DIFF_VIEWER.is(dataId)) {
1119 private class WrapperState implements ViewerState {
1120 @NotNull private final DiffViewer myViewer;
1121 @NotNull private final FrameDiffTool myTool;
1123 @NotNull private DiffViewer myWrapperViewer;
1125 public WrapperState(@NotNull DiffViewer viewer, @NotNull FrameDiffTool tool, @NotNull DiffViewerWrapper wrapper) {
1128 myWrapperViewer = wrapper.createComponent(myContext, myActiveRequest, myViewer);
1133 public void init() {
1134 myContentPanel.setContent(myWrapperViewer.getComponent());
1135 setTitle(myActiveRequest.getTitle());
1138 FrameDiffTool.ToolbarComponents toolbarComponents1 = myViewer.init();
1139 FrameDiffTool.ToolbarComponents toolbarComponents2 = myWrapperViewer.init();
1141 List<AnAction> toolbarActions = new ArrayList<AnAction>();
1142 if (toolbarComponents1.toolbarActions != null) toolbarActions.addAll(toolbarComponents1.toolbarActions);
1143 if (toolbarComponents2.toolbarActions != null) {
1144 if (!toolbarActions.isEmpty() && !toolbarComponents2.toolbarActions.isEmpty()) toolbarActions.add(Separator.getInstance());
1145 toolbarActions.addAll(toolbarComponents2.toolbarActions);
1147 buildToolbar(toolbarActions);
1149 List<AnAction> popupActions = new ArrayList<AnAction>();
1150 if (toolbarComponents1.popupActions != null) popupActions.addAll(toolbarComponents1.popupActions);
1151 if (toolbarComponents2.popupActions != null) {
1152 if (!popupActions.isEmpty() && !toolbarComponents2.popupActions.isEmpty()) popupActions.add(Separator.getInstance());
1153 popupActions.addAll(toolbarComponents2.popupActions);
1155 buildActionPopup(popupActions);
1158 myToolbarStatusPanel.setContent(toolbarComponents1.statusPanel); // TODO: combine both panels ?
1163 public void destroy() {
1164 Disposer.dispose(myViewer);
1165 Disposer.dispose(myWrapperViewer);
1170 public JComponent getPreferredFocusedComponent() {
1171 return myWrapperViewer.getPreferredFocusedComponent();
1176 public DiffTool getActiveTool() {
1182 public Object getData(@NonNls String dataId) {
1183 if (DiffDataKeys.DIFF_VIEWER.is(dataId)) {
1184 return myWrapperViewer;