git log refactoring, common way to details loading.

This commit is contained in:
irengrig
2011-07-05 01:39:56 +04:00
parent 8683405468
commit eb251f61f7
19 changed files with 1181 additions and 405 deletions
@@ -12,6 +12,7 @@ import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Map;
/**
* @author oleg
@@ -75,6 +76,12 @@ public class CheckBoxList extends JBList {
});
}
public void setStringItems(final Map<String, Boolean> items) {
for (Map.Entry<String, Boolean> entry : items.entrySet()) {
((DefaultListModel) getModel()).addElement(new JCheckBox(entry.getKey(), entry.getValue()));
}
}
public boolean isItemSelected(int index) {
return ((JCheckBox)getModel().getElementAt(index)).isSelected();
}
@@ -99,6 +106,9 @@ public class CheckBoxList extends JBList {
this.checkBoxListListener = checkBoxListListener;
}
protected void adjustRendering(final JCheckBox checkBox, final boolean selected, final boolean hasFocus) {
}
private class CellRenderer implements ListCellRenderer {
private final Border mySelectedBorder;
private final Border myBorder;
@@ -119,8 +129,8 @@ public class CheckBoxList extends JBList {
checkbox.setFont(getFont(checkbox));
checkbox.setFocusPainted(false);
checkbox.setBorderPainted(true);
checkbox.setBorder(isSelected ? mySelectedBorder : myBorder);
adjustRendering(checkbox, isSelected, cellHasFocus);
return checkbox;
}
}
@@ -0,0 +1,60 @@
/*
* Copyright 2000-2011 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.vcs;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.util.Comparing;
import com.intellij.util.Consumer;
import com.intellij.util.PairConsumer;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.atomic.AtomicReference;
/**
* @author irengrig
* Date: 6/29/11
* Time: 11:38 PM
*/
public class GenericDetailsLoader<Id, Data> implements PairConsumer<Id, Data> {
private final Consumer<Id> myLoader;
private final ValueConsumer<Id, Data> myValueConsumer;
private final AtomicReference<Id> myCurrentlySelected;
public GenericDetailsLoader(final Consumer<Id> loader, final PairConsumer<Id, Data> valueConsumer) {
myLoader = loader;
myValueConsumer = new ValueConsumer<Id, Data>(valueConsumer);
myCurrentlySelected = new AtomicReference<Id>(null);
}
public void updateSelection(@Nullable final Id id) {
ApplicationManager.getApplication().assertIsDispatchThread();
myValueConsumer.setId(id);
if (! Comparing.equal(id, myCurrentlySelected.getAndSet(id))) {
myLoader.consume(id);
}
}
@Override
public void consume(Id id, Data data) {
ApplicationManager.getApplication().assertIsDispatchThread();
myValueConsumer.consume(id, data);
}
public Id getCurrentlySelected() {
return myCurrentlySelected.get();
}
}
@@ -0,0 +1,45 @@
/*
* Copyright 2000-2011 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.vcs;
import com.intellij.util.PairConsumer;
/**
* @author irengrig
* Date: 6/29/11
* Time: 11:51 PM
*/
public class ValueConsumer<Id, Data> {
private Id myId;
private Id mySetId;
private final PairConsumer<Id, Data> myConsumer;
protected ValueConsumer(PairConsumer<Id, Data> consumer) {
myConsumer = consumer;
}
public void consume(final Id id, final Data data) {
if (id.equals(mySetId)) return; // already set
if (! id.equals(myId)) return;
mySetId = id;
myConsumer.consume(id, data);
}
public void setId(Id id) {
myId = id;
mySetId = null;
}
}
@@ -0,0 +1,41 @@
/*
* Copyright 2000-2011 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.vcsUtil;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.util.ui.UIUtil;
import javax.swing.*;
import java.awt.*;
/**
* @author irengrig
* Date: 7/1/11
* Time: 2:49 PM
*/
public class UIVcsUtil {
private UIVcsUtil() {
}
public static JPanel errorPanel(final String text, boolean isError) {
final JLabel label = new JLabel(text);
label.setForeground(isError ? SimpleTextAttributes.ERROR_ATTRIBUTES.getFgColor() : UIUtil.getInactiveTextColor());
final JPanel wrapper = new JPanel(new GridBagLayout());
wrapper.add(label, new GridBagConstraints(0,0,1,1,0,0,GridBagConstraints.CENTER, GridBagConstraints.NONE,
new Insets(1,1,1,1), 0,0));
return wrapper;
}
}
@@ -59,6 +59,7 @@ import com.intellij.util.containers.SLRUMap;
import com.intellij.util.messages.MessageBusConnection;
import com.intellij.util.ui.UIUtil;
import com.intellij.util.ui.tree.TreeUtil;
import com.intellij.vcsUtil.UIVcsUtil;
import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -120,9 +121,9 @@ public class ChangesViewManager implements ChangesViewI, JDOMExternalizable, Pro
myContentManager = contentManager;
myVcsChangeDetailsManager = vcsChangeDetailsManager;
myView = new ChangesListView(project);
myNoDetailsPanel = VcsChangeDetailsManager.errorPanel("No details available", false);
myNothingSelected = VcsChangeDetailsManager.errorPanel("Nothing selected", false);
myNotLoadedYet = VcsChangeDetailsManager.errorPanel("Changes content is not loaded yet", false);
myNoDetailsPanel = UIVcsUtil.errorPanel("No details available", false);
myNothingSelected = UIVcsUtil.errorPanel("Nothing selected", false);
myNotLoadedYet = UIVcsUtil.errorPanel("Changes content is not loaded yet", false);
Disposer.register(project, myView);
myRepaintAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD, project);
@@ -263,7 +264,7 @@ public class ChangesViewManager implements ChangesViewI, JDOMExternalizable, Pro
} else {
final String freezed = ChangeListManager.getInstance(myProject).isFreezed();
if (freezed != null) {
setChangeDetailsPanel(VcsChangeDetailsManager.errorPanel(freezed, false));
setChangeDetailsPanel(UIVcsUtil.errorPanel(freezed, false));
return;
}
Pair<JPanel, Disposable> details = null;
@@ -27,12 +27,11 @@ import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.VcsKey;
import com.intellij.openapi.vcs.changes.actions.DiffRequestFromChange;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.util.BeforeAfter;
import com.intellij.util.containers.Convertor;
import com.intellij.util.containers.HashMap;
import com.intellij.util.ui.UIUtil;
import com.intellij.vcsUtil.UIVcsUtil;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
@@ -104,7 +103,7 @@ public class VcsChangeDetailsManager {
contents = myRequestFromChange.createRequestForChange(o, 0);
}
catch (VcsException e) {
return new Pair<JPanel, Disposable>(errorPanel(e.getMessage(), true), null);
return new Pair<JPanel, Disposable>(UIVcsUtil.errorPanel(e.getMessage(), true), null);
}
if (contents == null || contents.isEmpty()) return null;
assert contents.size() == 1;
@@ -155,11 +154,11 @@ public class VcsChangeDetailsManager {
if (requestForChange == null) return null;
if (requestForChange.isEmpty()) {
return new Pair<JPanel, Disposable>(
errorPanel(DiffBundle.message("diff.contents.have.differences.only.in.line.separators.message.text"), false), null);
UIVcsUtil.errorPanel(DiffBundle.message("diff.contents.have.differences.only.in.line.separators.message.text"), false), null);
}
}
catch (VcsException e) {
return new Pair<JPanel, Disposable>(errorPanel(e.getMessage(), true), null);
return new Pair<JPanel, Disposable>(UIVcsUtil.errorPanel(e.getMessage(), true), null);
}
final ChangesFragmentedDiffPanel panel =
@@ -175,13 +174,4 @@ public class VcsChangeDetailsManager {
o.getBeforeRevision() == null
? "New" : o.getBeforeRevision().getRevisionNumber().asString()).append(")").toString();
}
public static JPanel errorPanel(final String text, boolean isError) {
final JLabel label = new JLabel(text);
label.setForeground(isError ? SimpleTextAttributes.ERROR_ATTRIBUTES.getFgColor() : UIUtil.getInactiveTextColor());
final JPanel wrapper = new JPanel(new GridBagLayout());
wrapper.add(label, new GridBagConstraints(0,0,1,1,0,0,GridBagConstraints.CENTER, GridBagConstraints.NONE,
new Insets(1,1,1,1), 0,0));
return wrapper;
}
}
+2
View File
@@ -112,6 +112,8 @@
<projectService
serviceInterface="git4idea.DialogManager"
serviceImplementation="git4idea.DialogManager"/>
<projectService serviceInterface="git4idea.history.wholeTree.GitLogSettings"
serviceImplementation="git4idea.history.wholeTree.GitLogSettings"/>
<applicationService
serviceInterface="git4idea.config.GitVcsApplicationSettings"
@@ -162,8 +162,12 @@ public class LowLevelAccessImpl implements LowLevelAccess {
}
public List<String> getBranchesWithCommit(final SHAHash hash) throws VcsException {
return getBranchesWithCommit(hash.getValue());
}
public List<String> getBranchesWithCommit(final String hash) throws VcsException {
final List<String> result = new ArrayList<String>();
GitBranch.listAsStrings(myProject, myRoot, true, true, result, hash.getValue());
GitBranch.listAsStrings(myProject, myRoot, true, true, result, hash);
//GitBranch.listAsStrings(myProject, myRoot, true, false, result, hash.getValue());
return result;
}
@@ -13,6 +13,7 @@
package git4idea.history.browser;
import git4idea.GitBranch;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.TreeSet;
@@ -66,6 +67,11 @@ public class SymbolicRefs {
return myTags;
}
@Nullable
public String getCurrentName() {
return myCurrent == null ? null : myCurrent.getName();
}
public GitBranch getCurrent() {
return myCurrent;
}
@@ -0,0 +1,192 @@
/*
* Copyright 2000-2011 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 git4idea.history.wholeTree;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.Disposer;
import com.intellij.ui.CheckBoxList;
import com.intellij.ui.ListSpeedSearch;
import com.intellij.ui.ScrollPaneFactory;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import java.awt.*;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.util.*;
/**
* @author irengrig
* Date: 6/30/11
* Time: 8:06 PM
*/
public class ContainedInBranchesConfigDialog extends DialogWrapper {
private JPanel myPanel;
private CheckBoxList myLocalBranches;
private CheckBoxList myRemoteBranches;
private final Project myProject;
private boolean myChanged;
public ContainedInBranchesConfigDialog(final Project project, final Collection<String> localBranches,
Collection<String> remoteBranches, final String currentLocal, final String currentRemote) {
super(project, true);
myProject = project;
setTitle("Configure branches to check reachability for");
initUi(project, localBranches, remoteBranches, currentLocal, currentRemote);
Disposer.register(myProject, getDisposable());
init();
}
@Override
protected String getDimensionServiceKey() {
return "git4idea.history.wholeTree.ContainedInBranchesConfigDialog";
}
private void initUi(Project project, Collection<String> localBranches, Collection<String> remoteBranches, final String currentLocal,
final String currentRemote) {
myPanel = new JPanel(new GridBagLayout());
final GridBagConstraints gb =
new GridBagConstraints(0, 0, 1, 1, 0, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(1, 1, 1, 1), 0, 0);
myLocalBranches = new MyCheckBoxList(currentLocal);
myRemoteBranches = new MyCheckBoxList(currentRemote);
gb.gridwidth = 2;
myPanel.add(new JLabel("Selected branches that will be used to check whether commit is reachable from (contained in)."), gb);
gb.insets.top = 10;
gb.gridwidth = 1;
++ gb.gridy;
myPanel.add(new JLabel("Local branches"), gb);
++ gb.gridx;
myPanel.add(new JLabel("Remote branches"), gb);
gb.insets.top = 0;
++ gb.gridy;
gb.gridx = 0;
gb.fill = GridBagConstraints.BOTH;
gb.weightx = 0.5;
gb.weighty = 1;
myLocalBranches.setBorder(null);
myRemoteBranches.setBorder(null);
myPanel.add(ScrollPaneFactory.createScrollPane(myLocalBranches), gb);
++ gb.gridx;
myPanel.add(ScrollPaneFactory.createScrollPane(myRemoteBranches), gb);
final GitLogSettings gitLogSettings = GitLogSettings.getInstance(project);
setItems(localBranches, gitLogSettings.getLocalBranchesCopy(), myLocalBranches);
setItems(remoteBranches, gitLogSettings.getRemoteBranchesCopy(), myRemoteBranches);
new ListSpeedSearch(myLocalBranches);
new ListSpeedSearch(myRemoteBranches);
focus(myLocalBranches);
focus(myRemoteBranches);
}
private void focus(final CheckBoxList list) {
list.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
if (list.getItemsCount() > 0 && list.getSelectedIndex() == -1) {
list.setSelectedIndex(0);
//list.removeSelectionInterval(0, 0);
list.removeFocusListener(this);
}
}
});
}
public boolean isChanged() {
return myChanged;
}
private static void setItems(Collection<String> localBranches, Set<String> localBranchesCopy, final CheckBoxList list) {
// order
final Map<String, Boolean> localBranchesState = new TreeMap<String, Boolean>();
for (String localBranch : localBranches) {
localBranchesState.put(localBranch, localBranchesCopy.contains(localBranch));
}
list.setStringItems(localBranchesState);
}
@Override
public JComponent getPreferredFocusedComponent() {
return myLocalBranches;
}
@Override
public Dimension getPreferredSize() {
return new Dimension(300, 400);
}
@Override
protected JComponent createCenterPanel() {
return myPanel;
}
@Override
protected void doOKAction() {
final GitLogSettings gitLogSettings = GitLogSettings.getInstance(myProject);
final ArrayList<String> local = gatherSelected((DefaultListModel)myLocalBranches.getModel());
final ArrayList<String> remote = gatherSelected((DefaultListModel)myRemoteBranches.getModel());
if (gitLogSettings.setIfChanged(local, remote)) {
myChanged = true;
}
super.doOKAction();
}
private ArrayList<String> gatherSelected(DefaultListModel localModel) {
final ArrayList<String> selected = new ArrayList<String>();
final Enumeration<?> elements = localModel.elements();
while (elements.hasMoreElements()) {
final JCheckBox cb = (JCheckBox)elements.nextElement();
if (cb.isSelected()) {
selected.add(cb.getText());
}
}
return selected;
}
private static class MyCheckBoxList extends CheckBoxList {
private final String myBold;
private final static Border FOCUSED_BORDER = UIManager.getBorder("List.focusCellHighlightBorder");
private final EmptyBorder myEmptyBorder;
private MyCheckBoxList(String bold) {
myBold = bold;
final Insets borderInsets = FOCUSED_BORDER.getBorderInsets(new JCheckBox());
myEmptyBorder = new EmptyBorder(borderInsets);
setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
}
@Override
protected void adjustRendering(JCheckBox checkBox, final boolean selected, final boolean hasFocus) {
checkBox.setFocusPainted(false);
if (myBold != null && myBold.equals(checkBox.getText())) {
checkBox.setFont(checkBox.getFont().deriveFont(Font.BOLD));
} else {
checkBox.setFont(checkBox.getFont().deriveFont(Font.PLAIN));
}
if (hasFocus) {
checkBox.setBorder(FOCUSED_BORDER);
} else {
checkBox.setBorder(myEmptyBorder);
}
}
}
}
@@ -21,14 +21,13 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.changes.BackgroundFromStartOption;
import com.intellij.openapi.vcs.changes.committed.AbstractCalledLater;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Consumer;
import com.intellij.util.Processor;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.containers.SLRUMap;
import git4idea.history.browser.GitCommit;
import git4idea.history.browser.LowLevelAccessImpl;
import git4idea.history.browser.SHAHash;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -49,7 +48,7 @@ public class DetailsCache {
private final DetailsLoaderImpl myDetailsLoader;
private final ModalityState myModalityState;
private final BackgroundTaskQueue myQueue;
private AbstractCalledLater myRefresh;
private UIRefresh myRefresh;
private final Map<VirtualFile, Map<AbstractHash, String>> myStash;
private final Object myLock;
private final static Logger LOG = Logger.getInstance("git4idea.history.wholeTree.DetailsCache");
@@ -64,12 +63,7 @@ public class DetailsCache {
myModalityState = modalityState;
myQueue = queue;
myStash = new HashMap<VirtualFile, Map<AbstractHash,String>>();
myRefresh = new AbstractCalledLater(project, myModalityState) {
@Override
public void run() {
uiRefresh.detailsLoaded();
}
};
myRefresh = uiRefresh;
myLock = new Object();
myCache = new SLRUMap<Pair<VirtualFile, AbstractHash>, GitCommit>(ourSize, 50);
myBranches = new SLRUMap<Pair<VirtualFile, AbstractHash>, List<String>>(10, 10);
@@ -92,7 +86,12 @@ public class DetailsCache {
myCache.put(new Pair<VirtualFile, AbstractHash>(root, commit.getShortHash()), commit);
}
}
myRefresh.callMe();
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
myRefresh.detailsLoaded();
}
});
}
public void rootsChanged(final Collection<VirtualFile> roots) {
@@ -133,17 +132,23 @@ public class DetailsCache {
}
}
public void clearBranches() {
synchronized (myLock) {
myBranches.clear();
}
}
public void loadAndPutBranches(final VirtualFile root,
final SHAHash hash,
final AbstractHash abstractHash,
final Consumer<List<String>> continuation) {
final Consumer<List<String>> continuation, final Processor<AbstractHash> recheck) {
myQueue.run(new Task.Backgroundable(myProject, "Load contained in branches", true, BackgroundFromStartOption.getInstance()) {
@Override
public void run(@NotNull ProgressIndicator indicator) {
if (! recheck.process(abstractHash)) return;
if (getBranches(root, abstractHash) != null) return;
final List<String> branches;
try {
branches = new LowLevelAccessImpl(myProject, root).getBranchesWithCommit(hash);
branches = new LowLevelAccessImpl(myProject, root).getBranchesWithCommit(abstractHash.getString());
}
catch (VcsException e) {
LOG.info(e);
@@ -0,0 +1,289 @@
/*
* Copyright 2000-2011 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 git4idea.history.wholeTree;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.changes.issueLinks.IssueLinkHtmlRenderer;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.BrowserHyperlinkListener;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.util.containers.Convertor;
import com.intellij.util.text.DateFormatUtil;
import com.intellij.util.ui.UIUtil;
import com.intellij.vcsUtil.UIVcsUtil;
import git4idea.history.browser.CaptionIcon;
import git4idea.history.browser.GitCommit;
import git4idea.history.browser.SymbolicRefs;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.event.HyperlinkEvent;
import java.awt.*;
/**
* @author irengrig
* Date: 7/1/11
* Time: 2:27 PM
*/
public class GitLogDetailsPanel {
public static final String CONFIGURE_BRANCHES = "configure_branches";
private final static String NOTHING_SELECTED = "nothingSelected";
private final static String LOADING = "loading";
private final static String DATA = "data";
private static final String MULTIPLE_SELECTED = "multiple_selected";
private final JPanel myPanel;
private JPanel myMarksPanel;
private final MyPresentationData myPresentationData;
private final HtmlHighlighter myHtmlHighlighter;
private JEditorPane myJEditorPane;
private VirtualFile myRoot;
public GitLogDetailsPanel(final Project myProject, final DetailsCache detailsCache, final Convertor<VirtualFile, SymbolicRefs> refsProvider) {
myPanel = new JPanel(new CardLayout());
myPanel.add(UIVcsUtil.errorPanel("Nothing selected", false), NOTHING_SELECTED);
myPanel.add(UIVcsUtil.errorPanel("Loading...", false), LOADING);
myPanel.add(UIVcsUtil.errorPanel("Several commits selected", false), MULTIPLE_SELECTED);
myHtmlHighlighter = new HtmlHighlighter();
myPresentationData = new MyPresentationData(myProject, detailsCache, myHtmlHighlighter);
final JPanel wrapper = new JPanel(new GridBagLayout());
final GridBagConstraints gb = new GridBagConstraints(0, 0, 1, 1, 0, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL,
new Insets(1, 1, 1, 1), 0, 0);
myJEditorPane = new JEditorPane(UIUtil.HTML_MIME, "");
myJEditorPane.setPreferredSize(new Dimension(150, 100));
myJEditorPane.setEditable(false);
myJEditorPane.setBackground(UIUtil.getComboBoxDisabledBackground());
myJEditorPane.addHyperlinkListener(new BrowserHyperlinkListener() {
@Override
public void hyperlinkUpdate(HyperlinkEvent e) {
/*if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED && CONFIGURE_BRANCHES.equals(e.getDescription())) {
if (myRoot == null) return;
final SymbolicRefs symbolicRefs = refsProvider.convert(myRoot);
if (symbolicRefs == null) return;
final TreeSet<String> localBranches = symbolicRefs.getLocalBranches();
if (localBranches == null || localBranches.isEmpty()) {
VcsBalloonProblemNotifier.showOverChangesView(myProject, "Branches is not loaded yet", MessageType.WARNING);
}
final ContainedInBranchesConfigDialog dialog =
new ContainedInBranchesConfigDialog(myProject, localBranches, symbolicRefs.getRemoteBranches(),
symbolicRefs.getCurrentName(), symbolicRefs.getTrackedRemoteName());
dialog.show();
if (dialog.isChanged()) {
detailsCache.clearBranches();
}
return;
}*/
super.hyperlinkUpdate(e);
}
});
myMarksPanel = new JPanel();
final BoxLayout boxLayout = new BoxLayout(myMarksPanel, BoxLayout.X_AXIS);
myMarksPanel.setLayout(boxLayout);
gb.weightx = 1;
wrapper.add(myMarksPanel, gb);
++ gb.gridy;
gb.weighty = 1;
gb.fill = GridBagConstraints.BOTH;
final JBScrollPane tableScroll = new JBScrollPane(myJEditorPane);
tableScroll.setBorder(null);
myJEditorPane.setBorder(null);
wrapper.add(tableScroll, gb);
myJEditorPane.setBackground(UIUtil.getTableBackground());
myMarksPanel.setBackground(UIUtil.getTableBackground());
wrapper.setBackground(UIUtil.getTableBackground());
myPanel.add(wrapper, DATA);
((CardLayout) myPanel.getLayout()).show(myPanel, NOTHING_SELECTED);
}
public HtmlHighlighter getHtmlHighlighter() {
return myHtmlHighlighter;
}
public void severalSelected() {
myPresentationData.clear();
((CardLayout) myPanel.getLayout()).show(myPanel, MULTIPLE_SELECTED);
}
public void nothingSelected() {
myPresentationData.clear();
((CardLayout) myPanel.getLayout()).show(myPanel, NOTHING_SELECTED);
}
public void setBranches(final java.util.List<String> branchesList) {
myPresentationData.setBranches(branchesList);
changeDetailsText();
}
public void loading(VirtualFile root) {
myRoot = root;
myPresentationData.clear();
((CardLayout) myPanel.getLayout()).show(myPanel, LOADING);
}
public void setData(VirtualFile root, @NotNull final GitCommit commit) {
myRoot = root;
redrawBranchLabels(commit);
myPresentationData.setCommit(root, commit);
((CardLayout) myPanel.getLayout()).show(myPanel, DATA);
changeDetailsText();
}
private void changeDetailsText() {
if (myPresentationData.isReady()) {
myJEditorPane.setText(myPresentationData.getText());
myPanel.revalidate();
myPanel.repaint();
}
}
private void redrawBranchLabels(GitCommit commit) {
final Font tableFont = myJEditorPane.getFont();
final Font font = tableFont.deriveFont((float)(tableFont.getSize() - 1));
final String currentBranch = commit.getCurrentBranch();
myMarksPanel.removeAll();
for (String s : commit.getLocalBranches()) {
myMarksPanel.add(new JLabel(new CaptionIcon(GitLogUI.Colors.local, font, s, myMarksPanel, CaptionIcon.Form.SQUARE, false,
s.equals(currentBranch))));
}
for (String s : commit.getRemoteBranches()) {
myMarksPanel.add(new JLabel(new CaptionIcon(GitLogUI.Colors.remote, font, s, myMarksPanel, CaptionIcon.Form.SQUARE, false,
s.equals(currentBranch))));
}
for (String s : commit.getTags()) {
myMarksPanel.add(new JLabel(new CaptionIcon(GitLogUI.Colors.tag, font, s, myMarksPanel, CaptionIcon.Form.ROUNDED, false,
s.equals(currentBranch))));
}
}
public JPanel getComponent() {
return myPanel;
}
private static class HtmlHighlighter extends HighlightingRendererBase {
private final StringBuilder mySb;
private HtmlHighlighter() {
super();
mySb = new StringBuilder();
}
@Override
protected void highlight(String s) {
mySb.append("<font color=rgb(255,128,0)>").append(s).append("</font>");
}
@Override
protected void usual(String s) {
mySb.append(s);
}
public String getResult(final String text) {
mySb.setLength(0);
tryHighlight(text);
return mySb.toString();
}
}
private static class MyPresentationData {
private String myStartPattern;
private final String myEndPattern = "</td></tr></table></body></html>";
private String myBranches;
private final Project myProject;
private final DetailsCache myDetailsCache;
private final HtmlHighlighter myHighlighter;
private MyPresentationData(final Project project, final DetailsCache detailsCache, final HtmlHighlighter highlighter) {
myProject = project;
myDetailsCache = detailsCache;
myHighlighter = highlighter;
}
public void setCommit(final VirtualFile root, final GitCommit c) {
final String hash = myHighlighter.getResult(c.getHash().getValue());
final String author = myHighlighter.getResult(c.getAuthor());
final String committer = myHighlighter.getResult(c.getCommitter());
final String comment = IssueLinkHtmlRenderer.formatTextWithLinks(myProject, c.getDescription(),
new Convertor<String, String>() {
@Override
public String convert(String o) {
return myHighlighter.getResult(o);
}
});
final StringBuilder sb = new StringBuilder().append("<html><head>").append(UIUtil.getCssFontDeclaration(UIUtil.getLabelFont()))
.append("</head><body><table>");
final String stashName = myDetailsCache.getStashName(root, c.getShortHash());
if (! StringUtil.isEmptyOrSpaces(stashName)) {
sb.append("<tr valign=\"top\"><td><b>").append(stashName).append("</b></td><td></td></tr>");
}
sb.append("<tr valign=\"top\"><td><i>Hash:</i></td><td>").append(
hash).append("</td></tr>" + "<tr valign=\"top\"><td><i>Author:</i></td><td>")
.append(author).append(" (").append(c.getAuthorEmail()).append(") <i>at</i> ")
.append(DateFormatUtil.formatPrettyDateTime(c.getAuthorTime()))
.append("</td></tr>" + "<tr valign=\"top\"><td><i>Commiter:</i></td><td>")
.append(committer).append(" (").append(c.getComitterEmail()).append(") <i>at</i> ")
.append(DateFormatUtil.formatPrettyDateTime(c.getDate())).append(
"</td></tr>" + "<tr valign=\"top\"><td><i>Description:</i></td><td><b>")
.append(comment).append("</b></td></tr>");
sb.append("<tr valign=\"top\"><td><i>Contained in branches:</i></td><td>");
myStartPattern = sb.toString();
}
public void setBranches(final java.util.List<String> branches) {
final StringBuilder sb = new StringBuilder();
if (branches != null && (! branches.isEmpty())) {
for (int i = 0; i < branches.size(); i++) {
String s = branches.get(i);
sb.append(s);
if (i + 1 < branches.size()) {
sb.append(", ");
}
}
myBranches = sb.toString();
} else if (branches != null && branches.isEmpty()) {
myBranches = "<font color=gray>&lt;no branches&gt;</font><br/>";
} else {
myBranches = "<font color=gray>Loading...</font><br/>";
}
}
protected String getBranches() {
return myBranches == null ? "<font color=gray>Loading...</font>" : myBranches;
}
public boolean isReady() {
return myStartPattern != null;
}
public String getText() {
return myStartPattern + getBranches() + myEndPattern;
}
public void clear() {
myBranches = null;
}
}
}
@@ -0,0 +1,101 @@
/*
* Copyright 2000-2011 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 git4idea.history.wholeTree;
import com.intellij.openapi.components.*;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.util.PairConsumer;
import com.intellij.util.containers.HashSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
/**
* @author irengrig
* Date: 7/1/11
* Time: 1:11 PM
*/
@State(
name = "GitLogSettings",
storages = {
@Storage(
id ="other",
file = "$PROJECT_FILE$"
)
,@Storage(id = "dir", file = "$PROJECT_CONFIG_DIR$/vcs.xml", scheme = StorageScheme.DIRECTORY_BASED)
}
)
public class GitLogSettings implements PersistentStateComponent<GitLogSettings.MyState> {
private MyState myState = new MyState();
public boolean setIfChanged(ArrayList<String> local, ArrayList<String> remote) {
if (Comparing.haveEqualElements(myState.myContainedLocalBranches, local) && Comparing.haveEqualElements(myState.myContainedRemoteBranches, remote)) {
return false;
}
setLocal(local);
setRemote(remote);
return true;
}
public static class MyState {
public List<String> myContainedLocalBranches = new ArrayList<String>();
public List<String> myContainedRemoteBranches = new ArrayList<String>();
}
@Override
public MyState getState() {
return myState;
}
@Override
public void loadState(MyState state) {
myState = state;
}
public static GitLogSettings getInstance(final Project project) {
return ServiceManager.getService(project, GitLogSettings.class);
}
public Set<String> getLocalBranchesCopy() {
return myState.myContainedLocalBranches == null ? Collections.<String>emptySet() : new HashSet<String>(myState.myContainedLocalBranches);
}
public Set<String> getRemoteBranchesCopy() {
return myState.myContainedRemoteBranches == null ? Collections.<String>emptySet() : new HashSet<String>(myState.myContainedRemoteBranches);
}
public void iterateBranches(final PairConsumer<String, Boolean> consumer) {
for (String item : myState.myContainedLocalBranches) {
consumer.consume(item, true);
}
for (String item : myState.myContainedRemoteBranches) {
consumer.consume(item, false);
}
}
public void setLocal(final List<String> local) {
Collections.sort(local);
myState.myContainedLocalBranches = local;
}
public void setRemote(final List<String> branches) {
Collections.sort(branches);
myState.myContainedRemoteBranches = branches;
}
}
@@ -28,22 +28,23 @@ import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.IconLoader;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.AbstractFilterChildren;
import com.intellij.openapi.vcs.CheckSamePattern;
import com.intellij.openapi.vcs.ComparableComparator;
import com.intellij.openapi.vcs.VcsDataKeys;
import com.intellij.openapi.vcs.*;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.changes.committed.CommittedChangesTreeBrowser;
import com.intellij.openapi.vcs.changes.committed.RepositoryChangesBrowser;
import com.intellij.openapi.vcs.changes.issueLinks.IssueLinkHtmlRenderer;
import com.intellij.openapi.vcs.changes.issueLinks.IssueLinkRenderer;
import com.intellij.openapi.vcs.changes.issueLinks.TableLinkMouseListener;
import com.intellij.openapi.vcs.ui.SearchFieldAction;
import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.*;
import com.intellij.ui.ColoredTableCellRenderer;
import com.intellij.ui.PopupHandler;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.ui.table.JBTable;
import com.intellij.util.Consumer;
import com.intellij.util.PairConsumer;
import com.intellij.util.Processor;
import com.intellij.util.containers.Convertor;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.text.DateFormatUtil;
@@ -86,19 +87,19 @@ public class GitLogUI implements Disposable {
private JBTable myJBTable;
private RepositoryChangesBrowser myRepositoryChangesBrowser;
private boolean myDataBeingAdded;
private boolean myMissingSelectionData;
private CardLayout myRepoLayout;
private JPanel myRepoPanel;
private boolean myStarted;
// private JTextField myFilterField;
private String myPreviousFilter;
private List<String> mySearchContext;
private final CommentSearchContext myCommentSearchContext;
private List<String> myUsersSearchContext;
private String mySelectedBranch;
private BranchSelectorAction myBranchSelectorAction;
private MySpecificDetails myDetails;
private final DescriptionRenderer myDescriptionRenderer;
private final Speedometer mySelectionSpeedometer;
private GenericDetailsLoader<CommitI, GitCommit> myDetailsLoader;
private GenericDetailsLoader<CommitI, List<String>> myBranchesLoader;
private GitLogDetailsPanel myDetailsPanel;
private StepType myState;
private MoreAction myMoreAction;
@@ -107,24 +108,47 @@ public class GitLogUI implements Disposable {
private MyCherryPick myCherryPickAction;
private MyRefreshAction myRefreshAction;
private AnAction myCopyHashAction;
// todo group somewhere??
private Consumer<CommitI> myDetailsLoaderImpl;
private Consumer<CommitI> myBranchesLoaderImpl;
private final RequestsMerger mySelectionRequestsMerger;
private final TableCellRenderer myAuthorRenderer;
public GitLogUI(Project project, final Mediator mediator) {
myProject = project;
myMediator = mediator;
mySearchContext = new ArrayList<String>();
myCommentSearchContext = new CommentSearchContext();
myUsersSearchContext = new ArrayList<String>();
myRefs = new HashMap<VirtualFile, SymbolicRefs>();
myRecalculatedCommon = new SymbolicRefs();
myPreviousFilter = "";
myDetails = new MySpecificDetails(myProject);
myDescriptionRenderer = new DescriptionRenderer();
mySelectionSpeedometer = new Speedometer(20, 400);
myCommentSearchContext.addHighlighter(myDescriptionRenderer.myInner.myWorker);
mySelectionRequestsMerger = new RequestsMerger(new Runnable() {
@Override
public void run() {
selectionChanged();
}
}, new Consumer<Runnable>() {
@Override
public void consume(Runnable runnable) {
SwingUtilities.invokeLater(runnable);
}
});
createTableModel();
myState = StepType.CONTINUE;
initUiRefresh();
myAuthorRenderer = new HighLightingRenderer(HIGHLIGHT_TEXT_ATTRIBUTES, SimpleTextAttributes.REGULAR_ATTRIBUTES);
}
private void initUiRefresh() {
myUIRefresh = new UIRefresh() {
@Override
public void detailsLoaded() {
tryRefreshDetails();
fireTableRepaint();
}
@@ -273,31 +297,70 @@ public class GitLogUI implements Disposable {
}
});
/*final JComponent specificDetails = myDetails.create();
final Content specificDetailsContent = myUi.createContent("Specific0", specificDetails, "Details", PlatformIcons.UNSELECT_ALL_ICON, null);
myUi.addContent(specificDetailsContent, 0, PlaceInGrid.bottom, false);
repoContent.setCloseable(false);
repoContent.setPinned(true);
createDetailLoaders();
}
myUi.getDefaults().initTabDefaults(0, "Git log", null);*/
// todo should look like it, but the behaviour of search differs
/*new TableSpeedSearch(myJBTable, new Convertor<Object, String>() {
private void createDetailLoaders() {
myDetailsLoaderImpl = new Consumer<CommitI>() {
@Override
public String convert(Object o) {
if (o instanceof CommitI) {
return ((CommitI) o).getDecorationString();
public void consume(final CommitI commitI) {
if (commitI == null) return;
final GitCommit gitCommit = fullCommitPresentation(commitI);
if (gitCommit == null) {
final MultiMap<VirtualFile, AbstractHash> question = new MultiMap<VirtualFile, AbstractHash>();
question.putValue(commitI.selectRepository(myRootsUnderVcs), commitI.getHash());
myDetailsCache.acceptQuestion(question);
} else {
myDetailsLoader.consume(commitI, gitCommit);
}
return o == null ? null : o.toString();
}
});*/
};
myDetailsLoader = new GenericDetailsLoader<CommitI, GitCommit>(myDetailsLoaderImpl, new PairConsumer<CommitI, GitCommit>() {
@Override
public void consume(CommitI commitI, GitCommit commit) {
myDetailsPanel.setData(commitI.selectRepository(myRootsUnderVcs), commit);
}
});
//myUi.getDefaults().initTabDefaults(0, "Git log", );
/* myUi = RunnerLayoutUi.Factory.getInstance(project).create("Debug", "unknown!", sessionName, this);
myUi.getDefaults().initTabDefaults(0, "Debug", null);
myBranchesLoaderImpl = new Consumer<CommitI>() {
private Processor<AbstractHash> myRecheck;
myUi.getOptions().setTopToolbar(createTopToolbar(), ActionPlaces.DEBUGGER_TOOLBAR);
*/
{
myRecheck = new Processor<AbstractHash>() {
@Override
public boolean process(AbstractHash abstractHash) {
if (myBranchesLoader.getCurrentlySelected() == null) return false;
return Comparing.equal(myBranchesLoader.getCurrentlySelected().getHash(), abstractHash);
}
};
}
@Override
public void consume(final CommitI commitI) {
if (commitI == null) return;
final VirtualFile root = commitI.selectRepository(myRootsUnderVcs);
final List<String> branches = myDetailsCache.getBranches(root, commitI.getHash());
if (branches != null) {
myBranchesLoader.consume(commitI, branches);
return;
}
myDetailsCache.loadAndPutBranches(root, commitI.getHash(), new Consumer<List<String>>() {
@Override
public void consume(List<String> strings) {
if (myProject.isDisposed() || strings == null) return;
myBranchesLoader.consume(commitI, strings);
}
}, myRecheck);
}
};
myBranchesLoader = new GenericDetailsLoader<CommitI, List<String>>(myBranchesLoaderImpl, new PairConsumer<CommitI, List<String>>() {
@Override
public void consume(CommitI commitI, List<String> strings) {
myDetailsPanel.setBranches(strings);
}
});
}
private JComponent createRepositoryBrowserDetails() {
@@ -308,9 +371,7 @@ public class GitLogUI implements Disposable {
myJBTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
if (! myDataBeingAdded && (! e.getValueIsAdjusting())) {
selectionChanged();
}
mySelectionRequestsMerger.request();
}
});
myRepoPanel.add("main", myRepositoryChangesBrowser);
@@ -323,24 +384,87 @@ public class GitLogUI implements Disposable {
}
private void selectionChanged() {
mySelectionSpeedometer.event();
if (myDataBeingAdded) {
mySelectionRequestsMerger.request();
return;
}
final int[] rows = myJBTable.getSelectedRows();
myDetails.refresh(null, null);
selectionChangedForDetails(rows);
if (rows.length == 0) {
myRepoLayout.show(myRepoPanel, "empty");
myRepoPanel.repaint();
myMissingSelectionData = false;
return;
} else if (rows.length >= 10) {
myRepoLayout.show(myRepoPanel, "tooMuch");
myRepoPanel.repaint();
myMissingSelectionData = false;
return;
}
myRepoLayout.show(myRepoPanel, "loading");
myRepoPanel.repaint();
updateDetailsFromSelection();
gatherNotLoadedData();
}
private static class MeaningfulSelection {
private CommitI myCommitI;
private int myMeaningfulRows;
private MeaningfulSelection(int[] rows, final BigTableTableModel tableModel) {
myMeaningfulRows = 0;
for (int row : rows) {
myCommitI = tableModel.getCommitAt(row);
if (!myCommitI.holdsDecoration()) {
++myMeaningfulRows;
if (myMeaningfulRows > 1) break;
}
}
}
public CommitI getCommit() {
return myCommitI;
}
public int getMeaningfulRows() {
return myMeaningfulRows;
}
}
private void tryRefreshDetails() {
MeaningfulSelection meaningfulSelection = new MeaningfulSelection(myJBTable.getSelectedRows(), myTableModel);
if (meaningfulSelection.getMeaningfulRows() == 1) {
// still have one item selected which probably was not loaded
final CommitI commit = meaningfulSelection.getCommit();
myDetailsLoaderImpl.consume(commit);
myBranchesLoaderImpl.consume(commit);
}
}
private void selectionChangedForDetails(int[] rows) {
MeaningfulSelection meaningfulSelection = new MeaningfulSelection(rows, myTableModel);
int meaningfulRows = meaningfulSelection.getMeaningfulRows();
CommitI commitAt = meaningfulSelection.getCommit();
if (meaningfulRows == 0) {
myDetailsPanel.nothingSelected();
myDetailsLoader.updateSelection(null);
myBranchesLoader.updateSelection(null);
} else if (meaningfulRows == 1) {
final GitCommit commit = fullCommitPresentation(commitAt);
if (commit == null) {
myDetailsPanel.loading(commitAt.selectRepository(myRootsUnderVcs));
}
myDetailsLoader.updateSelection(commitAt);
myBranchesLoader.updateSelection(commitAt);
} else {
myDetailsPanel.severalSelected();
myDetailsLoader.updateSelection(null);
myBranchesLoader.updateSelection(null);
}
}
private GitCommit fullCommitPresentation(CommitI commitAt) {
return myDetailsCache.convert(commitAt.selectRepository(myRootsUnderVcs), commitAt.getHash());
}
private static JPanel panelWithCenteredText(final String text) {
@@ -353,71 +477,27 @@ public class GitLogUI implements Disposable {
return jPanel;
}
public void updateSelection() {
updateBranchesFor();
if (! myMissingSelectionData) return;
updateDetailsFromSelection();
public void updateByScroll() {
gatherNotLoadedData();
}
private void updateBranchesFor() {
final int[] rows = myJBTable.getSelectedRows();
if (rows.length == 1 && myDetails.isMissingBranchesInfo() && mySelectionSpeedometer.getSpeed() < 0.1) {
final CommitI commit = myTableModel.getCommitAt(rows[0]);
final VirtualFile root = commit.selectRepository(myRootsUnderVcs);
if (commit.holdsDecoration()) return;
final GitCommit gitCommit = myDetailsCache.convert(root, commit.getHash());
if (gitCommit == null) return;
final List<String> branches = myDetailsCache.getBranches(root, commit.getHash());
if (branches != null) {
myDetails.putBranches(root, gitCommit, branches);
}
final CommitI commitI = myTableModel.getCommitAt(rows[0]);
if (! commit.equals(commitI)) return;
myDetailsCache.loadAndPutBranches(root, gitCommit.getHash(), commit.getHash(), new Consumer<List<String>>() {
@Override
public void consume(List<String> strings) {
if (myProject.isDisposed()) return;
final int[] afterRows = myJBTable.getSelectedRows();
if (myDetails.isMissingBranchesInfo() && afterRows.length == 1 && afterRows[0] == rows[0]) {
final CommitI afterCommit = myTableModel.getCommitAt(rows[0]);
if (afterCommit.holdsDecoration() || (! afterCommit.equals(commit))) return;
myDetails.putBranches(root, gitCommit, strings);
}
}
});
}
}
private void updateDetailsFromSelection() {
private void gatherNotLoadedData() {
if (myDataBeingAdded) return;
myMissingSelectionData = false;
final int[] rows = myJBTable.getSelectedRows();
if (rows.length == 1) {
final CommitI commitI = myTableModel.getCommitAt(rows[0]);
if (commitI != null && (! commitI.holdsDecoration())) {
final VirtualFile root = commitI.selectRepository(myRootsUnderVcs);
final GitCommit convert = myDetailsCache.convert(root, commitI.getHash());
if (convert != null) {
myDetails.refresh(root, convert);
}
}
}
final List<GitCommit> commits = new ArrayList<GitCommit>();
final MultiMap<VirtualFile,AbstractHash> missingHashes = new MultiMap<VirtualFile, AbstractHash>();
for (int i = rows.length - 1; i >= 0; --i) {
final int row = rows[i];
final CommitI commitI = myTableModel.getCommitAt(row);
if (commitI == null || commitI.holdsDecoration()) continue;
VirtualFile root = commitI.selectRepository(myRootsUnderVcs);
AbstractHash hash = commitI.getHash();
final GitCommit details = myDetailsCache.convert(root, hash);
commits.add(details);
myMissingSelectionData |= details == null;
missingHashes.putValue(root, hash);
final GitCommit details = fullCommitPresentation(commitI);
if (details == null) {
missingHashes.putValue(commitI.selectRepository(myRootsUnderVcs), commitI.getHash());
} else if (missingHashes.isEmpty()) { // no sense in collecting commits when s
commits.add(details);
}
}
if (myMissingSelectionData) {
if (! missingHashes.isEmpty()) {
myDetailsCache.acceptQuestion(missingHashes);
return;
}
@@ -481,7 +561,7 @@ public class GitLogUI implements Disposable {
myMyChangeListener = new GitTableScrollChangeListener(myJBTable, myDetailsCache, myTableModel, new Runnable() {
@Override
public void run() {
updateSelection();
updateByScroll();
}
});
scrollPane.getViewport().addChangeListener(myMyChangeListener);
@@ -492,10 +572,15 @@ public class GitLogUI implements Disposable {
mainBorderWrapper.add(scrollPane, BorderLayout.CENTER);
mainBorderWrapper.setBorder(BorderFactory.createLineBorder(UIUtil.getBorderColor()));
wrapper.add(mainBorderWrapper, BorderLayout.CENTER);
final JComponent specificDetails = myDetails.create();
myDetailsPanel = new GitLogDetailsPanel(myProject, myDetailsCache, new Convertor<VirtualFile, SymbolicRefs>() {
@Override
public SymbolicRefs convert(VirtualFile o) {
return myRefs.get(o);
}
});
final JPanel borderWrapper = new JPanel(new BorderLayout());
borderWrapper.setBorder(BorderFactory.createLineBorder(UIUtil.getBorderColor()));
borderWrapper.add(specificDetails, BorderLayout.CENTER);
borderWrapper.add(myDetailsPanel.getComponent(), BorderLayout.CENTER);
final Splitter splitter = new Splitter(true, 0.6f);
splitter.setFirstComponent(wrapper);
@@ -604,7 +689,7 @@ public class GitLogUI implements Disposable {
if (rows.length != 1) return;
final CommitI commitAt = myTableModel.getCommitAt(rows[0]);
if (commitAt == null) return;
final GitCommit gitCommit = myDetailsCache.convert(commitAt.selectRepository(myRootsUnderVcs), commitAt.getHash());
final GitCommit gitCommit = fullCommitPresentation(commitAt);
if (gitCommit == null) return;
sink.put(key, gitCommit.getDescription());
}
@@ -615,7 +700,7 @@ public class GitLogUI implements Disposable {
private GitCommit getCommitAtRow(int row) {
final CommitI commitAt = myTableModel.getCommitAt(row);
if (commitAt == null) return null;
final GitCommit gitCommit = myDetailsCache.convert(commitAt.selectRepository(myRootsUnderVcs), commitAt.getHash());
final GitCommit gitCommit = fullCommitPresentation(commitAt);
if (gitCommit == null) return null;
return gitCommit;
}
@@ -647,20 +732,42 @@ public class GitLogUI implements Disposable {
return true;
}
private Pair<String, List<String>> preparse(String previousFilter) {
final String[] strings = previousFilter.split("[\\s]");
StringBuilder sb = new StringBuilder();
mySearchContext.clear();
final List<String> words = new ArrayList<String>();
for (String string : strings) {
if (string.trim().length() == 0) continue;
mySearchContext.add(string.toLowerCase());
final String word = StringUtil.escapeToRegexp(string);
sb.append(word).append(".*");
words.add(word);
private static class CommentSearchContext {
private final List<HighlightingRendererBase> myListeners;
private final List<String> mySearchContext;
private CommentSearchContext() {
mySearchContext = new ArrayList<String>();
myListeners = new ArrayList<HighlightingRendererBase>();
}
public void addHighlighter(final HighlightingRendererBase renderer) {
myListeners.add(renderer);
}
public void clear() {
mySearchContext.clear();
for (HighlightingRendererBase listener : myListeners) {
listener.setSearchContext(Collections.<String>emptyList());
}
}
public String preparse(String previousFilter) {
final String[] strings = previousFilter.split("[\\s]");
StringBuilder sb = new StringBuilder();
mySearchContext.clear();
for (String string : strings) {
if (string.trim().length() == 0) continue;
mySearchContext.add(string.toLowerCase());
final String word = StringUtil.escapeToRegexp(string);
sb.append(word).append(".*");
}
new SubstringsFilter().doFilter(mySearchContext);
for (HighlightingRendererBase listener : myListeners) {
listener.setSearchContext(mySearchContext);
}
return sb.toString();
}
new SubstringsFilter().doFilter(mySearchContext);
return new Pair<String, List<String>>(sb.toString(), words);
}
public static class SubstringsFilter extends AbstractFilterChildren<String> {
@@ -729,103 +836,17 @@ public class GitLogUI implements Disposable {
}
};
private static abstract class HighlightingRendererBase {
private final List<String> mySearchContext;
protected HighlightingRendererBase(List<String> searchContext) {
mySearchContext = searchContext;
}
protected abstract void usual(final String s);
protected abstract void highlight(final String s);
void tryHighlight(String text) {
final String lower = text.toLowerCase();
int idxFrom = 0;
while (idxFrom < text.length()) {
boolean adjusted = false;
int adjustedIdx = text.length() + 1;
int adjLen = 0;
for (String word : mySearchContext) {
final int next = lower.indexOf(word, idxFrom);
if ((next != -1) && (adjustedIdx > next)) {
adjustedIdx = next;
adjLen = word.length();
adjusted = true;
}
}
if (adjusted) {
if (idxFrom != adjustedIdx) {
usual(text.substring(idxFrom, adjustedIdx));
}
idxFrom = adjustedIdx + adjLen;
highlight(text.substring(adjustedIdx, idxFrom));
continue;
}
usual(text.substring(idxFrom));
return;
}
}
}
private class AuthorRenderer extends ColoredTableCellRenderer {
private final SimpleTextAttributes myCurrentUserAttributes;
private final SimpleTextAttributes myHighlightAttributes;
private final List<String> mySearchContext;
private final SimpleTextAttributes myUsualAttributes;
protected final HighlightingRendererBase myWorker;
private AuthorRenderer(SimpleTextAttributes currentUserAttributes,
SimpleTextAttributes highlightAttributes,
List<String> searchContext,
SimpleTextAttributes usualAttributes) {
myCurrentUserAttributes = currentUserAttributes;
myHighlightAttributes = highlightAttributes;
mySearchContext = searchContext;
myUsualAttributes = usualAttributes;
myWorker = new HighlightingRendererBase(searchContext) {
@Override
protected void usual(String s) {
append(s, myUsualAttributes);
}
@Override
protected void highlight(String s) {
append(s, myHighlightAttributes);
}
};
}
@Override
protected void customizeCellRenderer(JTable table, Object value, boolean selected, boolean hasFocus, int row, int column) {
setBackground(getLogicBackground(selected, row));
if (BigTableTableModel.LOADING == value) {
return;
}
final String text = value.toString();
if (mySearchContext.isEmpty()) {
append(text, myUsualAttributes);
return;
}
myWorker.tryHighlight(text);
}
}
private class HighLightingRenderer extends ColoredTableCellRenderer {
private final SimpleTextAttributes myHighlightAttributes;
private final List<String> mySearchContext;
private final SimpleTextAttributes myUsualAttributes;
private SimpleTextAttributes myUsualAttributesForRun;
protected final HighlightingRendererBase myWorker;
public HighLightingRenderer(SimpleTextAttributes highlightAttributes, SimpleTextAttributes usualAttributes,
final List<String> searchContext) {
public HighLightingRenderer(SimpleTextAttributes highlightAttributes, SimpleTextAttributes usualAttributes) {
myHighlightAttributes = highlightAttributes;
mySearchContext = searchContext;
myUsualAttributes = usualAttributes == null ? SimpleTextAttributes.REGULAR_ATTRIBUTES : usualAttributes;
myUsualAttributesForRun = myUsualAttributes;
myWorker = new HighlightingRendererBase(searchContext) {
myWorker = new HighlightingRendererBase() {
@Override
protected void usual(String s) {
append(s, myUsualAttributesForRun);
@@ -838,6 +859,10 @@ public class GitLogUI implements Disposable {
};
}
public HighlightingRendererBase getWorker() {
return myWorker;
}
@Override
protected void customizeCellRenderer(JTable table, Object value, boolean selected, boolean hasFocus, int row, int column) {
setBackground(getLogicBackground(selected, row));
@@ -847,7 +872,7 @@ public class GitLogUI implements Disposable {
final String text = value.toString();
myUsualAttributesForRun = isCurrentUser(row, text) ?
SimpleTextAttributes.merge(myUsualAttributes, SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES) : myUsualAttributes;
if (mySearchContext.isEmpty()) {
if (myWorker.isEmpty()) {
append(text, myUsualAttributesForRun);
return;
}
@@ -931,12 +956,7 @@ public class GitLogUI implements Disposable {
CaptionIcon.Form.SQUARE, plus, branch.equals(commit.getCurrentBranch()));
myBranchMap.put(branch, icon);
}
myCurrentWidth = icon.getIconWidth();
myPanel.removeAll();
myPanel.setBackground(getLogicBackground(isSelected, row));
myPanel.add(new JLabel(icon));
myInner.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
myPanel.add(myInner);
addOneIcon(table, value, isSelected, hasFocus, row, column, icon);
return myPanel;
}
if ((localSize + remoteSize == 0) && (tagsSize > 0)) {
@@ -947,12 +967,7 @@ public class GitLogUI implements Disposable {
tag, table, CaptionIcon.Form.ROUNDED, tagsSize > 1, false);
myTagMap.put(tag, icon);
}
myCurrentWidth = icon.getIconWidth();
myPanel.removeAll();
myPanel.setBackground(getLogicBackground(isSelected, row));
myPanel.add(new JLabel(icon));
myInner.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
myPanel.add(myInner);
addOneIcon(table, value, isSelected, hasFocus, row, column, icon);
return myPanel;
}
}
@@ -960,12 +975,21 @@ public class GitLogUI implements Disposable {
return myInner;
}
private void addOneIcon(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column, Icon icon) {
myCurrentWidth = icon.getIconWidth();
myPanel.removeAll();
myPanel.setBackground(getLogicBackground(isSelected, row));
myPanel.add(new JLabel(icon));
myInner.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
myPanel.add(myInner);
}
private class Inner extends HighLightingRenderer {
private final IssueLinkRenderer myIssueLinkRenderer;
private final Consumer<String> myConsumer;
private Inner() {
super(HIGHLIGHT_TEXT_ATTRIBUTES, null, mySearchContext);
super(HIGHLIGHT_TEXT_ATTRIBUTES, null);
myIssueLinkRenderer = new IssueLinkRenderer(myProject, this);
myConsumer = new Consumer<String>() {
@Override
@@ -992,10 +1016,8 @@ public class GitLogUI implements Disposable {
Color bkgColor;
final CommitI commitAt = myTableModel.getCommitAt(row);
GitCommit gitCommit = null;
VirtualFile root = null;
if (commitAt != null & (! commitAt.holdsDecoration())) {
root = commitAt.selectRepository(myRootsUnderVcs);
gitCommit = myDetailsCache.convert(root, commitAt.getHash());
if (commitAt != null && (! commitAt.holdsDecoration())) {
gitCommit = fullCommitPresentation(commitAt);
}
if (isSelected) {
@@ -1003,7 +1025,7 @@ public class GitLogUI implements Disposable {
} else {
bkgColor = UIUtil.getTableBackground();
if (gitCommit != null) {
if (myDetailsCache.getStashName(root, gitCommit.getShortHash()) != null) {
if (myDetailsCache.getStashName(commitAt.selectRepository(myRootsUnderVcs), gitCommit.getShortHash()) != null) {
bkgColor = Colors.stashed;
} else if (gitCommit.isOnLocal() && gitCommit.isOnTracked()) {
bkgColor = Colors.commonThisBranch;
@@ -1019,9 +1041,6 @@ public class GitLogUI implements Disposable {
private void initAuthor() {
AUTHOR = new ColumnInfo<Object, String>("Author") {
private final TableCellRenderer myRenderer = new HighLightingRenderer(HIGHLIGHT_TEXT_ATTRIBUTES,
SimpleTextAttributes.REGULAR_ATTRIBUTES, myUsersSearchContext);
@Override
public String valueOf(Object o) {
if (o instanceof GitCommit) {
@@ -1032,7 +1051,7 @@ public class GitLogUI implements Disposable {
@Override
public TableCellRenderer getRenderer(Object o) {
return myRenderer;
return myAuthorRenderer;
}
};
}
@@ -1096,7 +1115,7 @@ public class GitLogUI implements Disposable {
final Collection<String> startingPoints = mySelectedBranch == null ? Collections.<String>emptyList() : Collections.singletonList(mySelectedBranch);
myDescriptionRenderer.resetIcons();
final boolean commentFilterEmpty = StringUtil.isEmptyOrSpaces(myPreviousFilter);
mySearchContext.clear();
myCommentSearchContext.clear();
myUsersSearchContext.clear();
if (commentFilterEmpty && (myUserFilterI.myFilter == null)) {
@@ -1105,9 +1124,8 @@ public class GitLogUI implements Disposable {
} else {
ChangesFilter.Comment comment = null;
if (! commentFilterEmpty) {
final Pair<String, List<String>> preparse = preparse(myPreviousFilter);
final String first = preparse.getFirst();
comment = new ChangesFilter.Comment(first);
final String commentFilter = myCommentSearchContext.preparse(myPreviousFilter);
comment = new ChangesFilter.Comment(commentFilter);
}
Set<ChangesFilter.Filter> userFilters = null;
if (myUserFilterI.myFilter != null) {
@@ -1127,13 +1145,14 @@ public class GitLogUI implements Disposable {
myMediator.reload(new RootsHolder(myRootsUnderVcs), startingPoints, new GitLogFilters(comment, userFilters, null,
possibleReferencies));
}
myCommentSearchContext.addHighlighter(myDetailsPanel.getHtmlHighlighter());
updateMoreVisibility();
selectionChanged();
mySelectionRequestsMerger.request();
fireTableRepaint();
myTableModel.fireTableRowsDeleted(0, was);
}
private interface Colors {
interface Colors {
Color tag = new Color(241, 239, 158);
Color remote = new Color(188,188,252);
Color local = new Color(117,238,199);
@@ -1142,147 +1161,6 @@ public class GitLogUI implements Disposable {
Color stashed = new Color(225,225,225);
}
private class MySpecificDetails {
private JEditorPane myJEditorPane;
private JPanel myMarksPanel;
private BoxLayout myBoxLayout;
private final Project myProject;
private boolean myMissingBranchesInfo;
private MySpecificDetails(final Project project) {
myProject = project;
}
public JComponent create() {
myJEditorPane = new JEditorPane(UIUtil.HTML_MIME, "");
myJEditorPane.setPreferredSize(new Dimension(150, 100));
myJEditorPane.setEditable(false);
myJEditorPane.setBackground(UIUtil.getComboBoxDisabledBackground());
myJEditorPane.addHyperlinkListener(new BrowserHyperlinkListener());
myMarksPanel = new JPanel();
myBoxLayout = new BoxLayout(myMarksPanel, BoxLayout.X_AXIS);
myMarksPanel.setLayout(myBoxLayout);
final JPanel wrapper = new JPanel(new BorderLayout());
wrapper.add(myMarksPanel, BorderLayout.NORTH);
wrapper.add(myJEditorPane, BorderLayout.CENTER);
final Color color = UIUtil.getTableBackground();
myJEditorPane.setBackground(color);
wrapper.setBackground(color);
myMarksPanel.setBackground(color);
final JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(wrapper);
return scrollPane;
}
public void putBranches(VirtualFile root, final GitCommit commit, final List<String> branches) {
myMissingBranchesInfo = branches == null;
myJEditorPane.setText(parseDetails(root, commit, branches));
}
public boolean isMissingBranchesInfo() {
return myMissingBranchesInfo;
}
public void refresh(VirtualFile root, final GitCommit commit) {
if (commit == null) {
myJEditorPane.setText("");
myMarksPanel.removeAll();
myMissingBranchesInfo = false;
} else {
final List<String> branches = myDetailsCache.getBranches(root, commit.getShortHash());
myMissingBranchesInfo = branches == null;
final Font font = myJEditorPane.getFont().deriveFont((float) (myJEditorPane.getFont().getSize() - 1));
final String currentBranch = commit.getCurrentBranch();
myMarksPanel.removeAll();
for (String s : commit.getLocalBranches()) {
myMarksPanel.add(new JLabel(new CaptionIcon(Colors.local, font, s, myMarksPanel, CaptionIcon.Form.SQUARE, false,
s.equals(currentBranch))));
}
for (String s : commit.getRemoteBranches()) {
myMarksPanel.add(new JLabel(new CaptionIcon(Colors.remote, font, s, myMarksPanel, CaptionIcon.Form.SQUARE, false,
s.equals(currentBranch))));
}
for (String s : commit.getTags()) {
myMarksPanel.add(new JLabel(new CaptionIcon(Colors.tag, font, s, myMarksPanel, CaptionIcon.Form.ROUNDED, false,
s.equals(currentBranch))));
}
myMarksPanel.repaint();
myJEditorPane.setText(parseDetails(root, commit, branches));
}
}
private String parseDetails(VirtualFile root, final GitCommit c, final List<String> branches) {
final String hash = new HtmlHighlighter(c.getHash().getValue()).getResult();
final String author = new HtmlHighlighter(c.getAuthor()).getResult();
final String committer = new HtmlHighlighter(c.getCommitter()).getResult();
final String comment = IssueLinkHtmlRenderer.formatTextWithLinks(myProject, c.getDescription(),
new Convertor<String, String>() {
@Override
public String convert(String o) {
return new HtmlHighlighter(o).getResult();
}
});
final StringBuilder sb = new StringBuilder().append("<html><head>").append(UIUtil.getCssFontDeclaration(UIUtil.getLabelFont()))
.append("</head><body><table>");
final String stashName = myDetailsCache.getStashName(root, c.getShortHash());
if (! StringUtil.isEmptyOrSpaces(stashName)) {
sb.append("<tr valign=\"top\"><td><b>").append(stashName).append("</b></td><td></td></tr>");
}
sb.append("<tr valign=\"top\"><td><i>Hash:</i></td><td>").append(
hash).append("</td></tr>" + "<tr valign=\"top\"><td><i>Author:</i></td><td>")
.append(author).append(" (").append(c.getAuthorEmail()).append(") <i>at</i> ")
.append(DateFormatUtil.formatPrettyDateTime(c.getAuthorTime()))
.append("</td></tr>" + "<tr valign=\"top\"><td><i>Commiter:</i></td><td>")
.append(committer).append(" (").append(c.getComitterEmail()).append(") <i>at</i> ")
.append(DateFormatUtil.formatPrettyDateTime(c.getDate())).append(
"</td></tr>" + "<tr valign=\"top\"><td><i>Description:</i></td><td><b>")
.append(comment).append("</b></td></tr>");
sb.append("<tr valign=\"top\"><td><i>Contained in branches:<i></td><td>");
if (branches != null && (! branches.isEmpty())) {
for (int i = 0; i < branches.size(); i++) {
String s = branches.get(i);
sb.append(s);
if (i + 1 < branches.size()) {
sb.append(", ");
}
}
} else if (branches != null && branches.isEmpty()) {
sb.append("<font color=gray>&lt;no branches&gt;</font>");
} else {
sb.append("<font color=gray>Loading...</font>");
}
sb.append("</td></tr>");
sb.append("</table></body></html>");
return sb.toString();
}
}
private class HtmlHighlighter extends HighlightingRendererBase {
private final String myText;
private final StringBuilder mySb;
private HtmlHighlighter(String text) {
super(mySearchContext);
myText = text;
mySb = new StringBuilder();
}
@Override
protected void highlight(String s) {
mySb.append("<font color=rgb(255,128,0)>").append(s).append("</font>");
}
@Override
protected void usual(String s) {
mySb.append(s);
}
public String getResult() {
tryHighlight(myText);
return mySb.toString();
}
}
private class MyCherryPick extends DumbAwareAction {
private final Set<AbstractHash> myIdsInProgress;
@@ -69,4 +69,24 @@ public class GroupHeaderCommitDecorator implements CommitI {
public CommitI getDelegate() {
return myDelegate;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
GroupHeaderCommitDecorator that = (GroupHeaderCommitDecorator)o;
if (!myDecoration.equals(that.myDecoration)) return false;
if (!myDelegate.equals(that.myDelegate)) return false;
return true;
}
@Override
public int hashCode() {
int result = myDecoration.hashCode();
result = 31 * result + myDelegate.hashCode();
return result;
}
}
@@ -61,4 +61,24 @@ public class GroupHeaderDatePseudoCommit implements CommitI {
@Override
public void setWireNumber(int wireNumber) {
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
GroupHeaderDatePseudoCommit that = (GroupHeaderDatePseudoCommit)o;
if (myTime != that.myTime) return false;
if (!myGroupName.equals(that.myGroupName)) return false;
return true;
}
@Override
public int hashCode() {
int result = (int)(myTime ^ (myTime >>> 32));
result = 31 * result + myGroupName.hashCode();
return result;
}
}
@@ -0,0 +1,72 @@
/*
* Copyright 2000-2011 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 git4idea.history.wholeTree;
import java.util.ArrayList;
import java.util.List;
/**
* @author irengrig
* Date: 7/1/11
* Time: 2:37 PM
*/
abstract class HighlightingRendererBase {
private final List<String> mySearchContext;
HighlightingRendererBase() {
mySearchContext = new ArrayList<String>();
}
public void setSearchContext(final List<String> list) {
mySearchContext.clear();
mySearchContext.addAll(list);
}
protected abstract void usual(final String s);
protected abstract void highlight(final String s);
void tryHighlight(String text) {
final String lower = text.toLowerCase();
int idxFrom = 0;
while (idxFrom < text.length()) {
boolean adjusted = false;
int adjustedIdx = text.length() + 1;
int adjLen = 0;
for (String word : mySearchContext) {
final int next = lower.indexOf(word, idxFrom);
if ((next != -1) && (adjustedIdx > next)) {
adjustedIdx = next;
adjLen = word.length();
adjusted = true;
}
}
if (adjusted) {
if (idxFrom != adjustedIdx) {
usual(text.substring(idxFrom, adjustedIdx));
}
idxFrom = adjustedIdx + adjLen;
highlight(text.substring(adjustedIdx, idxFrom));
continue;
}
usual(text.substring(idxFrom));
return;
}
}
public boolean isEmpty() {
return mySearchContext == null || mySearchContext.isEmpty();
}
}
@@ -65,4 +65,24 @@ public class MultipleRepositoryCommitDecorator implements CommitI {
public <T> T selectRepository(List<T> repositories) {
return repositories.get(myRepositoryIdx);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MultipleRepositoryCommitDecorator that = (MultipleRepositoryCommitDecorator)o;
if (myRepositoryIdx != that.myRepositoryIdx) return false;
if (!myDelegate.equals(that.myDelegate)) return false;
return true;
}
@Override
public int hashCode() {
int result = myDelegate.hashCode();
result = 31 * result + myRepositoryIdx;
return result;
}
}
@@ -64,4 +64,24 @@ public class WireNumberCommitDecoration implements CommitI {
public void setWireNumber(int wireNumber) {
myWireNumber = wireNumber;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
WireNumberCommitDecoration that = (WireNumberCommitDecoration)o;
if (myWireNumber != that.myWireNumber) return false;
if (!myDelegate.equals(that.myDelegate)) return false;
return true;
}
@Override
public int hashCode() {
int result = myDelegate.hashCode();
result = 31 * result + myWireNumber;
return result;
}
}