James Moger
2012-02-16 3cc6e2de29a0fa33dd585e938e1614a6dd5f9755
commit | author | age
38688b 1 /*
JM 2  * Copyright 2011 gitblit.com.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.gitblit.client;
17
18 import java.awt.BorderLayout;
19 import java.awt.FlowLayout;
20 import java.awt.Insets;
e33b91 21 import java.awt.Rectangle;
38688b 22 import java.awt.event.ActionEvent;
JM 23 import java.awt.event.ActionListener;
24 import java.awt.event.MouseAdapter;
25 import java.awt.event.MouseEvent;
26 import java.io.IOException;
92e2df 27 import java.util.ArrayList;
5506b8 28 import java.util.Collections;
92e2df 29 import java.util.HashSet;
38688b 30 import java.util.List;
92e2df 31 import java.util.Set;
38688b 32
92e2df 33 import javax.swing.DefaultComboBoxModel;
38688b 34 import javax.swing.JButton;
92e2df 35 import javax.swing.JComboBox;
JM 36 import javax.swing.JLabel;
38688b 37 import javax.swing.JPanel;
JM 38 import javax.swing.JScrollPane;
39 import javax.swing.JTable;
92e2df 40 import javax.swing.RowFilter;
38688b 41 import javax.swing.event.ListSelectionEvent;
JM 42 import javax.swing.event.ListSelectionListener;
92e2df 43 import javax.swing.table.TableRowSorter;
38688b 44
ee458f 45 import com.gitblit.models.FeedEntryModel;
e493cf 46 import com.gitblit.models.FeedModel;
92e2df 47 import com.gitblit.utils.StringUtils;
38688b 48
JM 49 /**
50  * RSS Feeds Panel displays recent entries and launches the browser to view the
51  * commit. commitdiff, or tree of a commit.
52  * 
53  * @author James Moger
54  * 
55  */
56 public abstract class FeedsPanel extends JPanel {
57
58     private static final long serialVersionUID = 1L;
59
60     private final GitblitClient gitblit;
61
5506b8 62     private final String ALL = "*";
92e2df 63
ee458f 64     private FeedEntryTableModel tableModel;
92e2df 65
ee458f 66     private TableRowSorter<FeedEntryTableModel> defaultSorter;
38688b 67
JM 68     private HeaderPanel header;
69
70     private JTable table;
92e2df 71
JM 72     private DefaultComboBoxModel repositoryChoices;
5506b8 73
JM 74     private JComboBox repositorySelector;
75
76     private DefaultComboBoxModel authorChoices;
77
78     private JComboBox authorSelector;
38688b 79
e33b91 80     private int page;
JM 81
82     private JButton prev;
83
84     private JButton next;
85
38688b 86     public FeedsPanel(GitblitClient gitblit) {
JM 87         super();
88         this.gitblit = gitblit;
89         initialize();
90     }
91
92     private void initialize() {
e33b91 93
JM 94         prev = new JButton("<");
95         prev.setToolTipText(Translation.get("gb.pagePrevious"));
96         prev.setEnabled(false);
97         prev.addActionListener(new ActionListener() {
98             public void actionPerformed(ActionEvent e) {
99                 refreshFeeds(--page);
100             }
101         });
102
103         next = new JButton(">");
104         next.setToolTipText(Translation.get("gb.pageNext"));
105         next.setEnabled(false);
106         next.addActionListener(new ActionListener() {
107             public void actionPerformed(ActionEvent e) {
108                 refreshFeeds(++page);
109             }
110         });
111
38688b 112         JButton refreshFeeds = new JButton(Translation.get("gb.refresh"));
JM 113         refreshFeeds.addActionListener(new ActionListener() {
114             public void actionPerformed(ActionEvent e) {
e33b91 115                 refreshFeeds(0);
38688b 116             }
JM 117         });
118
119         final JButton viewCommit = new JButton(Translation.get("gb.view"));
120         viewCommit.setEnabled(false);
121         viewCommit.addActionListener(new ActionListener() {
122             public void actionPerformed(ActionEvent e) {
123                 viewCommit();
124             }
125         });
126
127         final JButton viewCommitDiff = new JButton(Translation.get("gb.commitdiff"));
128         viewCommitDiff.setEnabled(false);
129         viewCommitDiff.addActionListener(new ActionListener() {
130             public void actionPerformed(ActionEvent e) {
131                 viewCommitDiff();
132             }
133         });
134
135         final JButton viewTree = new JButton(Translation.get("gb.tree"));
136         viewTree.setEnabled(false);
137         viewTree.addActionListener(new ActionListener() {
138             public void actionPerformed(ActionEvent e) {
139                 viewTree();
140             }
141         });
142
143         JButton subscribeFeeds = new JButton(Translation.get("gb.subscribe") + "...");
144         subscribeFeeds.addActionListener(new ActionListener() {
145             public void actionPerformed(ActionEvent e) {
146                 subscribeFeeds(gitblit.getAvailableFeeds());
147             }
148         });
149
150         JPanel controls = new JPanel(new FlowLayout(FlowLayout.CENTER, Utils.MARGIN, 0));
151         controls.add(refreshFeeds);
152         controls.add(subscribeFeeds);
153         controls.add(viewCommit);
154         controls.add(viewCommitDiff);
155         controls.add(viewTree);
156
157         NameRenderer nameRenderer = new NameRenderer();
ee458f 158         tableModel = new FeedEntryTableModel();
609a16 159         header = new HeaderPanel(Translation.get("gb.activity"), "feed_16x16.png");
38688b 160         table = Utils.newTable(tableModel, Utils.DATE_FORMAT);
ee458f 161         defaultSorter = new TableRowSorter<FeedEntryTableModel>(tableModel);
JM 162         String name = table.getColumnName(FeedEntryTableModel.Columns.Author.ordinal());
38688b 163         table.setRowHeight(nameRenderer.getFont().getSize() + 8);
JM 164         table.getColumn(name).setCellRenderer(nameRenderer);
ee458f 165         name = table.getColumnName(FeedEntryTableModel.Columns.Repository.ordinal());
38688b 166         table.getColumn(name).setCellRenderer(nameRenderer);
JM 167
ee458f 168         name = table.getColumnName(FeedEntryTableModel.Columns.Branch.ordinal());
c25a1d 169         table.getColumn(name).setCellRenderer(new BranchRenderer());
609a16 170
ee458f 171         name = table.getColumnName(FeedEntryTableModel.Columns.Message.ordinal());
609a16 172         table.getColumn(name).setCellRenderer(new MessageRenderer(gitblit));
38688b 173
JM 174         table.addMouseListener(new MouseAdapter() {
175             public void mouseClicked(MouseEvent e) {
176                 if (e.getClickCount() == 2) {
177                     if (e.isControlDown()) {
178                         viewCommitDiff();
179                     } else {
180                         viewCommit();
181                     }
182                 }
183             }
184         });
185
186         table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
187             @Override
188             public void valueChanged(ListSelectionEvent e) {
189                 if (e.getValueIsAdjusting()) {
190                     return;
191                 }
192                 boolean singleSelection = table.getSelectedRowCount() == 1;
193                 viewCommit.setEnabled(singleSelection);
194                 viewCommitDiff.setEnabled(singleSelection);
195                 viewTree.setEnabled(singleSelection);
196             }
197         });
198
92e2df 199         repositoryChoices = new DefaultComboBoxModel();
5506b8 200         repositorySelector = new JComboBox(repositoryChoices);
92e2df 201         repositorySelector.setRenderer(nameRenderer);
JM 202         repositorySelector.setForeground(nameRenderer.getForeground());
203         repositorySelector.addActionListener(new ActionListener() {
204             public void actionPerformed(ActionEvent event) {
6477ce 205                 // repopulate the author list based on repository selection
JM 206                 // preserve author selection, if possible
207                 String selectedAuthor = null;
208                 if (authorSelector.getSelectedIndex() > -1) {
209                     selectedAuthor = authorSelector.getSelectedItem().toString();
210                 }
211                 updateAuthors();
212                 if (selectedAuthor != null) {
213                     if (authorChoices.getIndexOf(selectedAuthor) > -1) {
214                         authorChoices.setSelectedItem(selectedAuthor);
215                     }
216                 }
5506b8 217                 filterFeeds();
JM 218             }
219         });
220         authorChoices = new DefaultComboBoxModel();
221         authorSelector = new JComboBox(authorChoices);
222         authorSelector.setRenderer(nameRenderer);
223         authorSelector.setForeground(nameRenderer.getForeground());
224         authorSelector.addActionListener(new ActionListener() {
225             public void actionPerformed(ActionEvent event) {
226                 filterFeeds();
92e2df 227             }
JM 228         });
229         JPanel northControls = new JPanel(new FlowLayout(FlowLayout.LEFT, Utils.MARGIN, 0));
230         northControls.add(new JLabel(Translation.get("gb.repository")));
231         northControls.add(repositorySelector);
5506b8 232         northControls.add(new JLabel(Translation.get("gb.author")));
JM 233         northControls.add(authorSelector);
e33b91 234 //        northControls.add(prev);
JM 235 //        northControls.add(next);
92e2df 236
JM 237         JPanel northPanel = new JPanel(new BorderLayout(0, Utils.MARGIN));
238         northPanel.add(header, BorderLayout.NORTH);
239         northPanel.add(northControls, BorderLayout.CENTER);
240
38688b 241         setLayout(new BorderLayout(Utils.MARGIN, Utils.MARGIN));
92e2df 242         add(northPanel, BorderLayout.NORTH);
38688b 243         add(new JScrollPane(table), BorderLayout.CENTER);
JM 244         add(controls, BorderLayout.SOUTH);
245     }
246
247     @Override
248     public Insets getInsets() {
249         return Utils.INSETS;
250     }
251
e33b91 252     protected void refreshFeeds(final int page) {
JM 253         this.page = page;
609a16 254         GitblitWorker worker = new GitblitWorker(FeedsPanel.this, null) {
38688b 255             @Override
JM 256             protected Boolean doRequest() throws IOException {
e33b91 257                 gitblit.refreshSubscribedFeeds(page);
38688b 258                 return true;
JM 259             }
260
261             @Override
262             protected void onSuccess() {
263                 updateTable(false);
264             }
265         };
266         worker.execute();
267     }
268
269     protected abstract void subscribeFeeds(List<FeedModel> feeds);
270
271     protected void updateTable(boolean pack) {
272         tableModel.entries.clear();
273         tableModel.entries.addAll(gitblit.getSyndicatedEntries());
274         tableModel.fireTableDataChanged();
609a16 275         header.setText(Translation.get("gb.activity") + " ("
e33b91 276                 + gitblit.getSyndicatedEntries().size() + (page > 0 ? (", pg " + (page + 1)) : "")
JM 277                 + ")");
38688b 278         if (pack) {
JM 279             Utils.packColumns(table, Utils.MARGIN);
280         }
e33b91 281         table.scrollRectToVisible(new Rectangle(table.getCellRect(0, 0, true)));
JM 282
283         if (page == 0) {
284             // determine unique repositories
285             Set<String> uniqueRepositories = new HashSet<String>();
ee458f 286             for (FeedEntryModel entry : tableModel.entries) {
e33b91 287                 uniqueRepositories.add(entry.repository);
JM 288             }
289
290             // repositories
291             List<String> sortedRespositories = new ArrayList<String>(uniqueRepositories);
292             StringUtils.sortRepositorynames(sortedRespositories);
293             repositoryChoices.removeAllElements();
294             repositoryChoices.addElement(ALL);
295             for (String repo : sortedRespositories) {
296                 repositoryChoices.addElement(repo);
297             }
92e2df 298         }
5506b8 299
e33b91 300         // update pagination buttons
JM 301         next.setEnabled(tableModel.entries.size() > 0);
302         prev.setEnabled(page > 0);
6477ce 303     }
5506b8 304
6477ce 305     private void updateAuthors() {
bab9c9 306         String repository = ALL;
JM 307         if (repositorySelector.getSelectedIndex() > -1) {
308             repository = repositorySelector.getSelectedItem().toString();
309         }
6477ce 310
JM 311         // determine unique repositories and authors
312         Set<String> uniqueAuthors = new HashSet<String>();
ee458f 313         for (FeedEntryModel entry : tableModel.entries) {
6477ce 314             if (repository.equals(ALL) || entry.repository.equalsIgnoreCase(repository)) {
JM 315                 uniqueAuthors.add(entry.author);
316             }
317         }
5506b8 318         // authors
JM 319         List<String> sortedAuthors = new ArrayList<String>(uniqueAuthors);
320         Collections.sort(sortedAuthors);
321         authorChoices.removeAllElements();
322         authorChoices.addElement(ALL);
323         for (String author : sortedAuthors) {
324             authorChoices.addElement(author);
92e2df 325         }
38688b 326     }
JM 327
ee458f 328     protected FeedEntryModel getSelectedSyndicatedEntry() {
38688b 329         int viewRow = table.getSelectedRow();
JM 330         int modelRow = table.convertRowIndexToModel(viewRow);
ee458f 331         FeedEntryModel entry = tableModel.get(modelRow);
38688b 332         return entry;
JM 333     }
334
335     protected void viewCommit() {
ee458f 336         FeedEntryModel entry = getSelectedSyndicatedEntry();
38688b 337         Utils.browse(entry.link);
JM 338     }
339
340     protected void viewCommitDiff() {
ee458f 341         FeedEntryModel entry = getSelectedSyndicatedEntry();
38688b 342         Utils.browse(entry.link.replace("/commit/", "/commitdiff/"));
JM 343     }
344
345     protected void viewTree() {
ee458f 346         FeedEntryModel entry = getSelectedSyndicatedEntry();
38688b 347         Utils.browse(entry.link.replace("/commit/", "/tree/"));
JM 348     }
92e2df 349
5506b8 350     protected void filterFeeds() {
JM 351         final String repository;
352         if (repositorySelector.getSelectedIndex() > -1) {
353             repository = repositorySelector.getSelectedItem().toString();
354         } else {
355             repository = ALL;
356         }
357
358         final String author;
359         if (authorSelector.getSelectedIndex() > -1) {
360             author = authorSelector.getSelectedItem().toString();
361         } else {
362             author = ALL;
363         }
364
365         if (repository.equals(ALL) && author.equals(ALL)) {
92e2df 366             table.setRowSorter(defaultSorter);
JM 367             return;
368         }
ee458f 369         final int repositoryIndex = FeedEntryTableModel.Columns.Repository.ordinal();
JM 370         final int authorIndex = FeedEntryTableModel.Columns.Author.ordinal();
371         RowFilter<FeedEntryTableModel, Object> containsFilter;
5506b8 372         if (repository.equals(ALL)) {
JM 373             // author filter
ee458f 374             containsFilter = new RowFilter<FeedEntryTableModel, Object>() {
5506b8 375                 public boolean include(
ee458f 376                         Entry<? extends FeedEntryTableModel, ? extends Object> entry) {
5506b8 377                     return entry.getStringValue(authorIndex).equalsIgnoreCase(author);
JM 378                 }
379             };
380         } else if (author.equals(ALL)) {
381             // repository filter
ee458f 382             containsFilter = new RowFilter<FeedEntryTableModel, Object>() {
5506b8 383                 public boolean include(
ee458f 384                         Entry<? extends FeedEntryTableModel, ? extends Object> entry) {
5506b8 385                     return entry.getStringValue(repositoryIndex).equalsIgnoreCase(repository);
JM 386                 }
387             };
388         } else {
389             // repository-author filter
ee458f 390             containsFilter = new RowFilter<FeedEntryTableModel, Object>() {
5506b8 391                 public boolean include(
ee458f 392                         Entry<? extends FeedEntryTableModel, ? extends Object> entry) {
5506b8 393                     boolean authorMatch = entry.getStringValue(authorIndex)
JM 394                             .equalsIgnoreCase(author);
395                     boolean repositoryMatch = entry.getStringValue(repositoryIndex)
396                             .equalsIgnoreCase(repository);
397                     return authorMatch && repositoryMatch;
398                 }
399             };
400         }
ee458f 401         TableRowSorter<FeedEntryTableModel> sorter = new TableRowSorter<FeedEntryTableModel>(
92e2df 402                 tableModel);
JM 403         sorter.setRowFilter(containsFilter);
404         table.setRowSorter(sorter);
405     }
38688b 406 }