1 package org.jetbrains.idea.svn.difftool;
3 import com.intellij.diff.DiffContext;
4 import com.intellij.diff.FrameDiffTool.DiffViewer;
5 import com.intellij.diff.FrameDiffTool.ToolbarComponents;
6 import com.intellij.diff.contents.DiffContent;
7 import com.intellij.diff.contents.EmptyContent;
8 import com.intellij.diff.requests.DiffRequest;
9 import com.intellij.diff.requests.ErrorDiffRequest;
10 import com.intellij.diff.tools.ErrorDiffTool;
11 import com.intellij.diff.util.DiffUtil;
12 import com.intellij.ide.DataManager;
13 import com.intellij.ide.impl.DataManagerImpl;
14 import com.intellij.openapi.actionSystem.AnAction;
15 import com.intellij.openapi.actionSystem.AnActionEvent;
16 import com.intellij.openapi.actionSystem.DataProvider;
17 import com.intellij.openapi.actionSystem.ToggleAction;
18 import com.intellij.openapi.actionSystem.ex.ActionUtil;
19 import com.intellij.openapi.diagnostic.Logger;
20 import com.intellij.openapi.project.DumbAware;
21 import com.intellij.openapi.project.Project;
22 import com.intellij.openapi.ui.Divider;
23 import com.intellij.openapi.ui.Splitter;
24 import com.intellij.openapi.util.Comparing;
25 import com.intellij.openapi.util.Disposer;
26 import com.intellij.openapi.util.Key;
27 import com.intellij.ui.EditorNotificationPanel;
28 import com.intellij.ui.JBColor;
29 import com.intellij.ui.components.panels.Wrapper;
30 import com.intellij.util.containers.HashMap;
31 import com.intellij.util.ui.JBUI;
32 import com.intellij.util.ui.UIUtil;
33 import org.jetbrains.annotations.NonNls;
34 import org.jetbrains.annotations.NotNull;
35 import org.jetbrains.annotations.Nullable;
36 import org.jetbrains.idea.svn.difftool.SvnDiffSettingsHolder.SvnDiffSettings;
37 import org.jetbrains.idea.svn.difftool.properties.SvnPropertiesDiffRequest;
38 import org.jetbrains.idea.svn.difftool.properties.SvnPropertiesDiffViewer;
39 import org.jetbrains.idea.svn.properties.PropertyData;
40 import org.jetbrains.idea.svn.properties.PropertyValue;
44 import java.awt.event.FocusAdapter;
45 import java.awt.event.FocusEvent;
46 import java.awt.event.FocusListener;
47 import java.util.ArrayList;
48 import java.util.Collections;
49 import java.util.List;
52 public class SvnDiffViewer implements DiffViewer {
53 private static final Logger LOG = Logger.getInstance(SvnDiffViewer.class);
55 public static final Key<MyPropertyContext> PROPERTY_CONTEXT_KEY = Key.create("MyPropertyContext");
56 public static final Key<Boolean> FOCUSED_VIEWER_KEY = Key.create("SvnFocusedViewer");
58 @Nullable private final Project myProject;
60 @NotNull private final DiffContext myContext;
61 @NotNull private final DiffRequest myPropertyRequest;
63 @NotNull private final SvnDiffSettings mySettings;
65 @NotNull private final JPanel myPanel;
66 @NotNull private final Splitter mySplitter;
67 @NotNull private final Wrapper myNotificationPanel;
69 @NotNull private final DiffViewer myContentViewer;
70 @NotNull private final DiffViewer myPropertiesViewer;
72 @NotNull private final FocusListener myContentFocusListener = new MyFocusListener(false);
73 @NotNull private final FocusListener myPropertiesFocusListener = new MyFocusListener(true);
75 private boolean myPropertiesViewerFocused; // False - content viewer, True - properties
76 private boolean myDumbContentViewer;
78 public SvnDiffViewer(@NotNull DiffContext context, @NotNull DiffRequest propertyRequest, @NotNull DiffViewer wrappingViewer) {
79 myProject = context.getProject();
81 myPropertyRequest = propertyRequest;
82 myContentViewer = wrappingViewer;
84 myPropertyRequest.onAssigned(true);
86 mySettings = initSettings(context);
88 mySplitter = new MySplitter("Property Changes");
89 mySplitter.setProportion(mySettings.getSplitterProportion());
90 mySplitter.setFirstComponent(myContentViewer.getComponent());
92 myNotificationPanel = new Wrapper();
94 MyPropertyContext propertyContext = initPropertyContext(context);
95 myPropertiesViewer = createPropertiesViewer(propertyRequest, propertyContext);
97 myPanel = new JPanel(new BorderLayout());
98 myPanel.add(mySplitter, BorderLayout.CENTER);
99 myPanel.add(myNotificationPanel, BorderLayout.SOUTH);
100 DataManager.registerDataProvider(myPanel, new DataProvider() {
102 public Object getData(@NonNls String dataId) {
103 DataProvider propertiesDataProvider = DataManagerImpl.getDataProviderEx(myPropertiesViewer.getComponent());
104 DataProvider contentDataProvider = DataManagerImpl.getDataProviderEx(myContentViewer.getComponent());
105 DataProvider defaultDP = myPropertiesViewerFocused ? propertiesDataProvider : contentDataProvider;
106 DataProvider fallbackDP = myPropertiesViewerFocused ? contentDataProvider : propertiesDataProvider;
107 return DiffUtil.getData(defaultDP, fallbackDP, dataId);
111 updatePropertiesPanel();
115 private static DiffViewer createPropertiesViewer(@NotNull DiffRequest propertyRequest, @NotNull MyPropertyContext propertyContext) {
116 if (propertyRequest instanceof SvnPropertiesDiffRequest) {
117 return SvnPropertiesDiffViewer.create(propertyContext, ((SvnPropertiesDiffRequest)propertyRequest), true);
120 return ErrorDiffTool.INSTANCE.createComponent(propertyContext, propertyRequest);
126 public ToolbarComponents init() {
129 processContextHints();
131 ToolbarComponents properties = myPropertiesViewer.init();
132 ToolbarComponents components = new ToolbarComponents();
133 components.toolbarActions = createToolbar(properties.toolbarActions);
138 public void dispose() {
141 updateContextHints();
143 Disposer.dispose(myPropertiesViewer);
145 myPropertyRequest.onAssigned(false);
148 private void processContextHints() {
149 if (myContext.getUserData(FOCUSED_VIEWER_KEY) == Boolean.TRUE) myPropertiesViewerFocused = true;
150 myDumbContentViewer = myContentViewer.getPreferredFocusedComponent() == null;
153 private void updateContextHints() {
154 if (!myDumbContentViewer && !mySettings.isHideProperties()) myContext.putUserData(FOCUSED_VIEWER_KEY, myPropertiesViewerFocused);
155 mySettings.setSplitterProportion(mySplitter.getProportion());
163 private JComponent createNotification() {
164 if (myPropertyRequest instanceof ErrorDiffRequest) {
165 return createNotification(((ErrorDiffRequest)myPropertyRequest).getMessage());
168 List<DiffContent> contents = ((SvnPropertiesDiffRequest)myPropertyRequest).getContents();
170 Map<String, PropertyValue> before = getProperties(contents.get(0));
171 Map<String, PropertyValue> after = getProperties(contents.get(1));
173 if (before.isEmpty() && after.isEmpty()) return null;
175 if (!before.keySet().equals(after.keySet())) {
176 return createNotification("SVN Properties changed");
179 for (String key : before.keySet()) {
180 if (!Comparing.equal(before.get(key), after.get(key))) return createNotification("SVN Properties changed");
187 private static Map<String, PropertyValue> getProperties(@NotNull DiffContent content) {
188 if (content instanceof EmptyContent) return Collections.emptyMap();
190 List<PropertyData> properties = ((SvnPropertiesDiffRequest.PropertyContent)content).getProperties();
192 Map<String, PropertyValue> map = new HashMap<>();
194 for (PropertyData data : properties) {
195 if (map.containsKey(data.getName())) LOG.warn("Duplicated property: " + data.getName());
196 map.put(data.getName(), data.getValue());
203 private static JPanel createNotification(@NotNull String text) {
204 return new EditorNotificationPanel().text(text);
211 private void updatePropertiesPanel() {
212 boolean wasFocused = myContext.isFocused();
213 if (!mySettings.isHideProperties()) {
214 mySplitter.setSecondComponent(myPropertiesViewer.getComponent());
215 myNotificationPanel.setContent(null);
218 mySplitter.setSecondComponent(null);
219 myNotificationPanel.setContent(createNotification());
221 if (wasFocused) myContext.requestFocus();
225 private List<AnAction> createToolbar(@Nullable List<AnAction> propertiesActions) {
226 List<AnAction> result = new ArrayList<>();
228 if (propertiesActions != null) result.addAll(propertiesActions);
230 result.add(new ToggleHidePropertiesAction());
236 private static SvnDiffSettings initSettings(@NotNull DiffContext context) {
237 SvnDiffSettings settings = context.getUserData(SvnDiffSettingsHolder.KEY);
238 if (settings == null) {
239 settings = SvnDiffSettings.getSettings();
240 context.putUserData(SvnDiffSettingsHolder.KEY, settings);
246 private MyPropertyContext initPropertyContext(@NotNull DiffContext context) {
247 MyPropertyContext propertyContext = context.getUserData(PROPERTY_CONTEXT_KEY);
248 if (propertyContext == null) {
249 propertyContext = new MyPropertyContext();
250 context.putUserData(PROPERTY_CONTEXT_KEY, propertyContext);
252 return propertyContext;
255 private void installListeners() {
256 myContentViewer.getComponent().addFocusListener(myContentFocusListener);
257 myPropertiesViewer.getComponent().addFocusListener(myPropertiesFocusListener);
260 private void destroyListeners() {
261 myContentViewer.getComponent().removeFocusListener(myContentFocusListener);
262 myPropertiesViewer.getComponent().removeFocusListener(myPropertiesFocusListener);
271 public JComponent getComponent() {
277 public JComponent getPreferredFocusedComponent() {
278 if (myPropertiesViewerFocused) {
279 JComponent component = getPropertiesPreferredFocusedComponent();
280 if (component != null) return component;
281 return myContentViewer.getPreferredFocusedComponent();
284 JComponent component = myContentViewer.getPreferredFocusedComponent();
285 if (component != null) return component;
286 return getPropertiesPreferredFocusedComponent();
291 private JComponent getPropertiesPreferredFocusedComponent() {
292 if (mySettings.isHideProperties()) return null;
293 return myPropertiesViewer.getPreferredFocusedComponent();
300 private class ToggleHidePropertiesAction extends ToggleAction implements DumbAware {
301 public ToggleHidePropertiesAction() {
302 ActionUtil.copyFrom(this, "Subversion.TogglePropertiesDiff");
306 public boolean isSelected(AnActionEvent e) {
307 return !mySettings.isHideProperties();
311 public void setSelected(AnActionEvent e, boolean state) {
312 mySettings.setHideProperties(!state);
313 updatePropertiesPanel();
321 private class MyPropertyContext extends DiffContext {
324 public Project getProject() {
325 return myContext.getProject();
329 public boolean isWindowFocused() {
330 return myContext.isWindowFocused();
334 public boolean isFocused() {
335 return DiffUtil.isFocusedComponent(getProject(), myPropertiesViewer.getComponent());
339 public void requestFocus() {
340 DiffUtil.requestFocus(getProject(), myPropertiesViewer.getPreferredFocusedComponent());
344 private class MyFocusListener extends FocusAdapter {
345 private final boolean myValue;
347 public MyFocusListener(boolean value) {
352 public void focusGained(FocusEvent e) {
353 myPropertiesViewerFocused = myValue;
357 private static class MySplitter extends Splitter {
358 @NotNull private final String myLabelText;
360 public MySplitter(@NotNull String text) {
366 protected Divider createDivider() {
367 return new DividerImpl() {
369 public void setOrientation(boolean isVerticalSplit) {
370 if (!isVertical()) LOG.warn("unsupported state: splitter should be vertical");
374 setCursor(Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR));
376 JLabel label = new JLabel(myLabelText);
377 label.setFont(UIUtil.getOptionPaneMessageFont());
378 label.setForeground(UIUtil.getLabelForeground());
379 add(label, new GridBagConstraints(0, 0, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.NONE, JBUI.insets(2), 0, 0));
380 setDividerWidth(label.getPreferredSize().height + JBUI.scale(4));
387 protected void paintComponent(Graphics g) {
388 super.paintComponent(g);
389 g.setColor(JBColor.border());
390 UIUtil.drawLine(g, 0, 0, getWidth(), 0);