/* | |
* Copyright 2011 gitblit.com. | |
* | |
* 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.gitblit.client; | |
import java.awt.BorderLayout; | |
import java.awt.FlowLayout; | |
import java.awt.Insets; | |
import java.awt.Rectangle; | |
import java.awt.event.ActionEvent; | |
import java.awt.event.ActionListener; | |
import java.awt.event.MouseAdapter; | |
import java.awt.event.MouseEvent; | |
import java.io.IOException; | |
import java.util.ArrayList; | |
import java.util.Collections; | |
import java.util.HashSet; | |
import java.util.List; | |
import java.util.Set; | |
import javax.swing.DefaultComboBoxModel; | |
import javax.swing.JButton; | |
import javax.swing.JComboBox; | |
import javax.swing.JLabel; | |
import javax.swing.JPanel; | |
import javax.swing.JScrollPane; | |
import javax.swing.JTable; | |
import javax.swing.RowFilter; | |
import javax.swing.event.ListSelectionEvent; | |
import javax.swing.event.ListSelectionListener; | |
import javax.swing.table.TableRowSorter; | |
import com.gitblit.models.FeedEntryModel; | |
import com.gitblit.models.FeedModel; | |
import com.gitblit.utils.StringUtils; | |
/** | |
* RSS Feeds Panel displays recent entries and launches the browser to view the | |
* commit. commitdiff, or tree of a commit. | |
* | |
* @author James Moger | |
* | |
*/ | |
public abstract class FeedsPanel extends JPanel { | |
private static final long serialVersionUID = 1L; | |
private final GitblitClient gitblit; | |
private final String ALL = "*"; | |
private FeedEntryTableModel tableModel; | |
private TableRowSorter<FeedEntryTableModel> defaultSorter; | |
private HeaderPanel header; | |
private JTable table; | |
private DefaultComboBoxModel repositoryChoices; | |
private JComboBox repositorySelector; | |
private DefaultComboBoxModel authorChoices; | |
private JComboBox authorSelector; | |
private int page; | |
private JButton prev; | |
private JButton next; | |
public FeedsPanel(GitblitClient gitblit) { | |
super(); | |
this.gitblit = gitblit; | |
initialize(); | |
} | |
private void initialize() { | |
prev = new JButton("<"); | |
prev.setToolTipText(Translation.get("gb.pagePrevious")); | |
prev.setEnabled(false); | |
prev.addActionListener(new ActionListener() { | |
@Override | |
public void actionPerformed(ActionEvent e) { | |
refreshFeeds(--page); | |
} | |
}); | |
next = new JButton(">"); | |
next.setToolTipText(Translation.get("gb.pageNext")); | |
next.setEnabled(false); | |
next.addActionListener(new ActionListener() { | |
@Override | |
public void actionPerformed(ActionEvent e) { | |
refreshFeeds(++page); | |
} | |
}); | |
JButton refreshFeeds = new JButton(Translation.get("gb.refresh")); | |
refreshFeeds.addActionListener(new ActionListener() { | |
@Override | |
public void actionPerformed(ActionEvent e) { | |
refreshFeeds(0); | |
} | |
}); | |
final JButton viewCommit = new JButton(Translation.get("gb.view")); | |
viewCommit.setEnabled(false); | |
viewCommit.addActionListener(new ActionListener() { | |
@Override | |
public void actionPerformed(ActionEvent e) { | |
viewCommit(); | |
} | |
}); | |
final JButton viewCommitDiff = new JButton(Translation.get("gb.commitdiff")); | |
viewCommitDiff.setEnabled(false); | |
viewCommitDiff.addActionListener(new ActionListener() { | |
@Override | |
public void actionPerformed(ActionEvent e) { | |
viewCommitDiff(); | |
} | |
}); | |
final JButton viewTree = new JButton(Translation.get("gb.tree")); | |
viewTree.setEnabled(false); | |
viewTree.addActionListener(new ActionListener() { | |
@Override | |
public void actionPerformed(ActionEvent e) { | |
viewTree(); | |
} | |
}); | |
JButton subscribeFeeds = new JButton(Translation.get("gb.subscribe") + "..."); | |
subscribeFeeds.addActionListener(new ActionListener() { | |
@Override | |
public void actionPerformed(ActionEvent e) { | |
subscribeFeeds(gitblit.getAvailableFeeds()); | |
} | |
}); | |
JPanel controls = new JPanel(new FlowLayout(FlowLayout.CENTER, Utils.MARGIN, 0)); | |
controls.add(refreshFeeds); | |
controls.add(subscribeFeeds); | |
controls.add(viewCommit); | |
controls.add(viewCommitDiff); | |
controls.add(viewTree); | |
NameRenderer nameRenderer = new NameRenderer(); | |
tableModel = new FeedEntryTableModel(); | |
header = new HeaderPanel(Translation.get("gb.activity"), "feed_16x16.png"); | |
table = Utils.newTable(tableModel, Utils.DATE_FORMAT); | |
defaultSorter = new TableRowSorter<FeedEntryTableModel>(tableModel); | |
String name = table.getColumnName(FeedEntryTableModel.Columns.Author.ordinal()); | |
table.getColumn(name).setCellRenderer(nameRenderer); | |
name = table.getColumnName(FeedEntryTableModel.Columns.Repository.ordinal()); | |
table.getColumn(name).setCellRenderer(nameRenderer); | |
name = table.getColumnName(FeedEntryTableModel.Columns.Branch.ordinal()); | |
table.getColumn(name).setCellRenderer(new BranchRenderer()); | |
name = table.getColumnName(FeedEntryTableModel.Columns.Message.ordinal()); | |
table.getColumn(name).setCellRenderer(new MessageRenderer(gitblit)); | |
table.addMouseListener(new MouseAdapter() { | |
@Override | |
public void mouseClicked(MouseEvent e) { | |
if (e.getClickCount() == 2) { | |
if (e.isControlDown()) { | |
viewCommitDiff(); | |
} else { | |
viewCommit(); | |
} | |
} | |
} | |
}); | |
table.getSelectionModel().addListSelectionListener(new ListSelectionListener() { | |
@Override | |
public void valueChanged(ListSelectionEvent e) { | |
if (e.getValueIsAdjusting()) { | |
return; | |
} | |
boolean singleSelection = table.getSelectedRowCount() == 1; | |
viewCommit.setEnabled(singleSelection); | |
viewCommitDiff.setEnabled(singleSelection); | |
viewTree.setEnabled(singleSelection); | |
} | |
}); | |
repositoryChoices = new DefaultComboBoxModel(); | |
repositorySelector = new JComboBox(repositoryChoices); | |
repositorySelector.setRenderer(nameRenderer); | |
repositorySelector.setForeground(nameRenderer.getForeground()); | |
repositorySelector.addActionListener(new ActionListener() { | |
@Override | |
public void actionPerformed(ActionEvent event) { | |
// repopulate the author list based on repository selection | |
// preserve author selection, if possible | |
String selectedAuthor = null; | |
if (authorSelector.getSelectedIndex() > -1) { | |
selectedAuthor = authorSelector.getSelectedItem().toString(); | |
} | |
updateAuthors(); | |
if (selectedAuthor != null) { | |
if (authorChoices.getIndexOf(selectedAuthor) > -1) { | |
authorChoices.setSelectedItem(selectedAuthor); | |
} | |
} | |
filterFeeds(); | |
} | |
}); | |
authorChoices = new DefaultComboBoxModel(); | |
authorSelector = new JComboBox(authorChoices); | |
authorSelector.setRenderer(nameRenderer); | |
authorSelector.setForeground(nameRenderer.getForeground()); | |
authorSelector.addActionListener(new ActionListener() { | |
@Override | |
public void actionPerformed(ActionEvent event) { | |
filterFeeds(); | |
} | |
}); | |
JPanel northControls = new JPanel(new FlowLayout(FlowLayout.LEFT, Utils.MARGIN, 0)); | |
northControls.add(new JLabel(Translation.get("gb.repository"))); | |
northControls.add(repositorySelector); | |
northControls.add(new JLabel(Translation.get("gb.author"))); | |
northControls.add(authorSelector); | |
// northControls.add(prev); | |
// northControls.add(next); | |
JPanel northPanel = new JPanel(new BorderLayout(0, Utils.MARGIN)); | |
northPanel.add(header, BorderLayout.NORTH); | |
northPanel.add(northControls, BorderLayout.CENTER); | |
setLayout(new BorderLayout(Utils.MARGIN, Utils.MARGIN)); | |
add(northPanel, BorderLayout.NORTH); | |
add(new JScrollPane(table), BorderLayout.CENTER); | |
add(controls, BorderLayout.SOUTH); | |
} | |
@Override | |
public Insets getInsets() { | |
return Utils.INSETS; | |
} | |
protected void refreshFeeds(final int page) { | |
this.page = page; | |
GitblitWorker worker = new GitblitWorker(FeedsPanel.this, null) { | |
@Override | |
protected Boolean doRequest() throws IOException { | |
gitblit.refreshSubscribedFeeds(page); | |
return true; | |
} | |
@Override | |
protected void onSuccess() { | |
updateTable(false); | |
} | |
}; | |
worker.execute(); | |
} | |
protected abstract void subscribeFeeds(List<FeedModel> feeds); | |
protected void updateTable(boolean pack) { | |
tableModel.entries.clear(); | |
tableModel.entries.addAll(gitblit.getSyndicatedEntries()); | |
tableModel.fireTableDataChanged(); | |
header.setText(Translation.get("gb.activity") + " (" | |
+ gitblit.getSyndicatedEntries().size() + (page > 0 ? (", pg " + (page + 1)) : "") | |
+ ")"); | |
if (pack) { | |
Utils.packColumns(table, Utils.MARGIN); | |
} | |
table.scrollRectToVisible(new Rectangle(table.getCellRect(0, 0, true))); | |
if (page == 0) { | |
// determine unique repositories | |
Set<String> uniqueRepositories = new HashSet<String>(); | |
for (FeedEntryModel entry : tableModel.entries) { | |
uniqueRepositories.add(entry.repository); | |
} | |
// repositories | |
List<String> sortedRespositories = new ArrayList<String>(uniqueRepositories); | |
StringUtils.sortRepositorynames(sortedRespositories); | |
repositoryChoices.removeAllElements(); | |
repositoryChoices.addElement(ALL); | |
for (String repo : sortedRespositories) { | |
repositoryChoices.addElement(repo); | |
} | |
} | |
// update pagination buttons | |
next.setEnabled(tableModel.entries.size() > 0); | |
prev.setEnabled(page > 0); | |
} | |
private void updateAuthors() { | |
String repository = ALL; | |
if (repositorySelector.getSelectedIndex() > -1) { | |
repository = repositorySelector.getSelectedItem().toString(); | |
} | |
// determine unique repositories and authors | |
Set<String> uniqueAuthors = new HashSet<String>(); | |
for (FeedEntryModel entry : tableModel.entries) { | |
if (repository.equals(ALL) || entry.repository.equalsIgnoreCase(repository)) { | |
uniqueAuthors.add(entry.author); | |
} | |
} | |
// authors | |
List<String> sortedAuthors = new ArrayList<String>(uniqueAuthors); | |
Collections.sort(sortedAuthors); | |
authorChoices.removeAllElements(); | |
authorChoices.addElement(ALL); | |
for (String author : sortedAuthors) { | |
authorChoices.addElement(author); | |
} | |
} | |
protected FeedEntryModel getSelectedSyndicatedEntry() { | |
int viewRow = table.getSelectedRow(); | |
int modelRow = table.convertRowIndexToModel(viewRow); | |
FeedEntryModel entry = tableModel.get(modelRow); | |
return entry; | |
} | |
protected void viewCommit() { | |
FeedEntryModel entry = getSelectedSyndicatedEntry(); | |
Utils.browse(entry.link); | |
} | |
protected void viewCommitDiff() { | |
FeedEntryModel entry = getSelectedSyndicatedEntry(); | |
Utils.browse(entry.link.replace("/commit/", "/commitdiff/")); | |
} | |
protected void viewTree() { | |
FeedEntryModel entry = getSelectedSyndicatedEntry(); | |
Utils.browse(entry.link.replace("/commit/", "/tree/")); | |
} | |
protected void filterFeeds() { | |
final String repository; | |
if (repositorySelector.getSelectedIndex() > -1) { | |
repository = repositorySelector.getSelectedItem().toString(); | |
} else { | |
repository = ALL; | |
} | |
final String author; | |
if (authorSelector.getSelectedIndex() > -1) { | |
author = authorSelector.getSelectedItem().toString(); | |
} else { | |
author = ALL; | |
} | |
if (repository.equals(ALL) && author.equals(ALL)) { | |
table.setRowSorter(defaultSorter); | |
return; | |
} | |
final int repositoryIndex = FeedEntryTableModel.Columns.Repository.ordinal(); | |
final int authorIndex = FeedEntryTableModel.Columns.Author.ordinal(); | |
RowFilter<FeedEntryTableModel, Object> containsFilter; | |
if (repository.equals(ALL)) { | |
// author filter | |
containsFilter = new RowFilter<FeedEntryTableModel, Object>() { | |
@Override | |
public boolean include( | |
Entry<? extends FeedEntryTableModel, ? extends Object> entry) { | |
return entry.getStringValue(authorIndex).equalsIgnoreCase(author); | |
} | |
}; | |
} else if (author.equals(ALL)) { | |
// repository filter | |
containsFilter = new RowFilter<FeedEntryTableModel, Object>() { | |
@Override | |
public boolean include( | |
Entry<? extends FeedEntryTableModel, ? extends Object> entry) { | |
return entry.getStringValue(repositoryIndex).equalsIgnoreCase(repository); | |
} | |
}; | |
} else { | |
// repository-author filter | |
containsFilter = new RowFilter<FeedEntryTableModel, Object>() { | |
@Override | |
public boolean include( | |
Entry<? extends FeedEntryTableModel, ? extends Object> entry) { | |
boolean authorMatch = entry.getStringValue(authorIndex) | |
.equalsIgnoreCase(author); | |
boolean repositoryMatch = entry.getStringValue(repositoryIndex) | |
.equalsIgnoreCase(repository); | |
return authorMatch && repositoryMatch; | |
} | |
}; | |
} | |
TableRowSorter<FeedEntryTableModel> sorter = new TableRowSorter<FeedEntryTableModel>( | |
tableModel); | |
sorter.setRowFilter(containsFilter); | |
table.setRowSorter(sorter); | |
} | |
} |