James Moger
2012-02-16 3cc6e2de29a0fa33dd585e938e1614a6dd5f9755
commit | author | age
9119cf 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;
c7a721 19 import java.awt.Cursor;
9119cf 20 import java.awt.FlowLayout;
JM 21 import java.awt.Insets;
e33b91 22 import java.awt.Rectangle;
9119cf 23 import java.awt.event.ActionEvent;
JM 24 import java.awt.event.ActionListener;
25 import java.awt.event.MouseAdapter;
26 import java.awt.event.MouseEvent;
27 import java.awt.event.WindowAdapter;
28 import java.awt.event.WindowEvent;
29 import java.io.IOException;
30 import java.util.List;
31
32 import javax.swing.DefaultComboBoxModel;
33 import javax.swing.ImageIcon;
34 import javax.swing.JButton;
35 import javax.swing.JComboBox;
36 import javax.swing.JFrame;
37 import javax.swing.JLabel;
38 import javax.swing.JPanel;
39 import javax.swing.JScrollPane;
40 import javax.swing.JTable;
41 import javax.swing.JTextField;
42 import javax.swing.SwingWorker;
43 import javax.swing.event.ListSelectionEvent;
44 import javax.swing.event.ListSelectionListener;
45
46 import com.gitblit.Constants;
ee458f 47 import com.gitblit.models.FeedEntryModel;
e493cf 48 import com.gitblit.models.RepositoryModel;
9119cf 49 import com.gitblit.utils.StringUtils;
JM 50
51 /**
52  * The search dialog allows searching of a repository branch. This matches the
53  * search implementation of the site.
54  * 
55  * @author James Moger
56  * 
57  */
58 public class SearchDialog extends JFrame {
59
60     private static final long serialVersionUID = 1L;
61
ee458f 62     private final boolean isSearch;
JM 63
9119cf 64     private final GitblitClient gitblit;
JM 65
ee458f 66     private FeedEntryTableModel tableModel;
9119cf 67
JM 68     private HeaderPanel header;
69
70     private JTable table;
71
72     private JComboBox repositorySelector;
73
74     private DefaultComboBoxModel branchChoices;
75
76     private JComboBox branchSelector;
77
78     private JComboBox searchTypeSelector;
79
80     private JTextField searchFragment;
81
82     private JComboBox maxHitsSelector;
83
e33b91 84     private int page;
JM 85
86     private JButton prev;
87
88     private JButton next;
89
ee458f 90     public SearchDialog(GitblitClient gitblit, boolean isSearch) {
9119cf 91         super();
JM 92         this.gitblit = gitblit;
ee458f 93         this.isSearch = isSearch;
JM 94         setTitle(Translation.get(isSearch ? "gb.search" : "gb.log"));
c7e7e9 95         setIconImage(new ImageIcon(getClass().getResource("/gitblt-favicon.png")).getImage());
9119cf 96         initialize();
ee458f 97         setSize(900, 550);
9119cf 98     }
JM 99
100     private void initialize() {
101
e33b91 102         prev = new JButton("<");
JM 103         prev.setToolTipText(Translation.get("gb.pagePrevious"));
104         prev.setEnabled(false);
105         prev.addActionListener(new ActionListener() {
106             public void actionPerformed(ActionEvent e) {
107                 search(--page);
108             }
109         });
110
111         next = new JButton(">");
112         next.setToolTipText(Translation.get("gb.pageNext"));
113         next.setEnabled(false);
114         next.addActionListener(new ActionListener() {
115             public void actionPerformed(ActionEvent e) {
116                 search(++page);
117             }
118         });
119
ee458f 120         final JButton search = new JButton(Translation.get(isSearch ? "gb.search" : "gb.refresh"));
9119cf 121         search.addActionListener(new ActionListener() {
JM 122             public void actionPerformed(ActionEvent e) {
e33b91 123                 search(0);
9119cf 124             }
JM 125         });
126
127         final JButton viewCommit = new JButton(Translation.get("gb.view"));
128         viewCommit.setEnabled(false);
129         viewCommit.addActionListener(new ActionListener() {
130             public void actionPerformed(ActionEvent e) {
131                 viewCommit();
132             }
133         });
134
135         final JButton viewCommitDiff = new JButton(Translation.get("gb.commitdiff"));
136         viewCommitDiff.setEnabled(false);
137         viewCommitDiff.addActionListener(new ActionListener() {
138             public void actionPerformed(ActionEvent e) {
139                 viewCommitDiff();
140             }
141         });
142
143         final JButton viewTree = new JButton(Translation.get("gb.tree"));
144         viewTree.setEnabled(false);
145         viewTree.addActionListener(new ActionListener() {
146             public void actionPerformed(ActionEvent e) {
147                 viewTree();
148             }
149         });
150
151         JPanel controls = new JPanel(new FlowLayout(FlowLayout.CENTER, Utils.MARGIN, 0));
152         controls.add(viewCommit);
153         controls.add(viewCommitDiff);
154         controls.add(viewTree);
155
156         NameRenderer nameRenderer = new NameRenderer();
ee458f 157         tableModel = new FeedEntryTableModel();
JM 158         header = new HeaderPanel(Translation.get(isSearch ? "gb.search" : "gb.log"),
159                 isSearch ? "search-icon.png" : "commit_changes_16x16.png");
9119cf 160         table = Utils.newTable(tableModel, Utils.DATE_FORMAT);
JM 161
ee458f 162         String name = table.getColumnName(FeedEntryTableModel.Columns.Author.ordinal());
9119cf 163         table.setRowHeight(nameRenderer.getFont().getSize() + 8);
JM 164         table.getColumn(name).setCellRenderer(nameRenderer);
ee458f 165         name = table.getColumnName(FeedEntryTableModel.Columns.Repository.ordinal());
9119cf 166         table.getColumn(name).setCellRenderer(nameRenderer);
JM 167
ee458f 168         name = table.getColumnName(FeedEntryTableModel.Columns.Branch.ordinal());
9119cf 169         table.getColumn(name).setCellRenderer(new BranchRenderer());
JM 170
ee458f 171         name = table.getColumnName(FeedEntryTableModel.Columns.Message.ordinal());
9119cf 172         table.getColumn(name).setCellRenderer(new MessageRenderer());
JM 173
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
199         repositorySelector = new JComboBox(gitblit.getRepositories().toArray());
200         repositorySelector.setRenderer(nameRenderer);
201         repositorySelector.setForeground(nameRenderer.getForeground());
202         repositorySelector.addActionListener(new ActionListener() {
203             public void actionPerformed(ActionEvent event) {
204                 // repopulate the branch list based on repository selection
205                 // preserve branch selection, if possible
206                 String selectedBranch = null;
207                 if (branchSelector.getSelectedIndex() > -1) {
208                     selectedBranch = branchSelector.getSelectedItem().toString();
209                 }
210                 updateBranches();
41cffb 211                 if (StringUtils.isEmpty(selectedBranch)) {
JM 212                     // do not select branch
213                     branchSelector.setSelectedIndex(-1);
214                 } else {
9119cf 215                     if (branchChoices.getIndexOf(selectedBranch) > -1) {
41cffb 216                         // select branch
9119cf 217                         branchChoices.setSelectedItem(selectedBranch);
41cffb 218                     } else {
JM 219                         // branch does not exist, do not select branch
220                         branchSelector.setSelectedIndex(-1);
9119cf 221                     }
JM 222                 }
223             }
224         });
225
226         branchChoices = new DefaultComboBoxModel();
227         branchSelector = new JComboBox(branchChoices);
ee458f 228         branchSelector.setRenderer(new BranchRenderer());
9119cf 229
JM 230         searchTypeSelector = new JComboBox(Constants.SearchType.values());
231         searchTypeSelector.setSelectedItem(Constants.SearchType.COMMIT);
232
233         maxHitsSelector = new JComboBox(new Integer[] { 25, 50, 75, 100 });
e33b91 234         maxHitsSelector.setSelectedIndex(0);
9119cf 235
JM 236         searchFragment = new JTextField(25);
237         searchFragment.addActionListener(new ActionListener() {
238             public void actionPerformed(ActionEvent event) {
e33b91 239                 search(0);
9119cf 240             }
JM 241         });
242
41cffb 243         JPanel queryPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, Utils.MARGIN, 0));
JM 244         queryPanel.add(new JLabel(Translation.get("gb.repository")));
245         queryPanel.add(repositorySelector);
246         queryPanel.add(new JLabel(Translation.get("gb.branch")));
247         queryPanel.add(branchSelector);
ee458f 248         if (isSearch) {
JM 249             queryPanel.add(new JLabel(Translation.get("gb.type")));
250             queryPanel.add(searchTypeSelector);
251         }
41cffb 252         queryPanel.add(new JLabel(Translation.get("gb.maxHits")));
JM 253         queryPanel.add(maxHitsSelector);
254
255         JPanel actionsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, Utils.MARGIN, 0));
256         actionsPanel.add(search);
257         actionsPanel.add(prev);
258         actionsPanel.add(next);
259
260         JPanel northControls = new JPanel(new BorderLayout(Utils.MARGIN, Utils.MARGIN));
261         northControls.add(queryPanel, BorderLayout.WEST);
ee458f 262         if (isSearch) {
JM 263             northControls.add(searchFragment, BorderLayout.CENTER);
264         }
41cffb 265         northControls.add(actionsPanel, BorderLayout.EAST);
9119cf 266
JM 267         JPanel northPanel = new JPanel(new BorderLayout(0, Utils.MARGIN));
268         northPanel.add(header, BorderLayout.NORTH);
269         northPanel.add(northControls, BorderLayout.CENTER);
270
271         JPanel contentPanel = new JPanel() {
272
273             private static final long serialVersionUID = 1L;
274
275             @Override
276             public Insets getInsets() {
277                 return Utils.INSETS;
278             }
279         };
280         contentPanel.setLayout(new BorderLayout(Utils.MARGIN, Utils.MARGIN));
281         contentPanel.add(northPanel, BorderLayout.NORTH);
282         contentPanel.add(new JScrollPane(table), BorderLayout.CENTER);
283         contentPanel.add(controls, BorderLayout.SOUTH);
284         setLayout(new BorderLayout());
285         add(contentPanel, BorderLayout.CENTER);
a568f2 286         addWindowListener(new WindowAdapter() {
JM 287             @Override
288             public void windowOpened(WindowEvent event) {
289                 if (isSearch) {
ee458f 290                     searchFragment.requestFocus();
a568f2 291                 } else {
JM 292                     search(0);
ee458f 293                 }
a568f2 294             }
9119cf 295
a568f2 296             @Override
JM 297             public void windowActivated(WindowEvent event) {
298                 if (isSearch) {
ee458f 299                     searchFragment.requestFocus();
JM 300                 }
a568f2 301             }
JM 302         });
9119cf 303     }
JM 304
305     public void selectRepository(RepositoryModel repository) {
306         repositorySelector.setSelectedItem(repository);
307     }
308
309     private void updateBranches() {
310         String repository = null;
311         if (repositorySelector.getSelectedIndex() > -1) {
312             repository = repositorySelector.getSelectedItem().toString();
313         }
314         List<String> branches = gitblit.getBranches(repository);
315         branchChoices.removeAllElements();
316         for (String branch : branches) {
317             branchChoices.addElement(branch);
318         }
319     }
320
e33b91 321     protected void search(final int page) {
JM 322         this.page = page;
9119cf 323         final String repository = repositorySelector.getSelectedItem().toString();
JM 324         final String branch = branchSelector.getSelectedIndex() > -1 ? branchSelector
325                 .getSelectedItem().toString() : null;
326         final Constants.SearchType searchType = (Constants.SearchType) searchTypeSelector
327                 .getSelectedItem();
ee458f 328         final String fragment = isSearch ? searchFragment.getText() : null;
9119cf 329         final int maxEntryCount = maxHitsSelector.getSelectedIndex() > -1 ? ((Integer) maxHitsSelector
JM 330                 .getSelectedItem()) : -1;
e33b91 331
ee458f 332         if (isSearch && StringUtils.isEmpty(fragment)) {
9119cf 333             return;
JM 334         }
c7a721 335         setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
ee458f 336         SwingWorker<List<FeedEntryModel>, Void> worker = new SwingWorker<List<FeedEntryModel>, Void>() {
9119cf 337             @Override
ee458f 338             protected List<FeedEntryModel> doInBackground() throws IOException {
JM 339                 if (isSearch) {
340                     return gitblit.search(repository, branch, fragment, searchType, maxEntryCount,
341                             page);
342                 } else {
343                     return gitblit.log(repository, branch, maxEntryCount, page);
344                 }
9119cf 345             }
JM 346
347             @Override
348             protected void done() {
c7a721 349                 setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
9119cf 350                 try {
ee458f 351                     List<FeedEntryModel> results = get();
JM 352                     if (isSearch) {
353                         updateTable(true, fragment, results);
354                     } else {
355                         updateTable(true, branch == null ? "" : branch, results);
356                     }
9119cf 357                 } catch (Throwable t) {
JM 358                     Utils.showException(SearchDialog.this, t);
359                 }
360             }
361         };
362         worker.execute();
363     }
364
ee458f 365     protected void updateTable(boolean pack, String text, List<FeedEntryModel> entries) {
9119cf 366         tableModel.entries.clear();
JM 367         tableModel.entries.addAll(entries);
368         tableModel.fireTableDataChanged();
a568f2 369         setTitle(Translation.get(isSearch ? "gb.search" : "gb.log")
JM 370                 + (StringUtils.isEmpty(text) ? "" : (": " + text)) + " (" + entries.size()
371                 + (page > 0 ? (", pg " + (page + 1)) : "") + ")");
9119cf 372         header.setText(getTitle());
JM 373         if (pack) {
374             Utils.packColumns(table, Utils.MARGIN);
375         }
e33b91 376         table.scrollRectToVisible(new Rectangle(table.getCellRect(0, 0, true)));
JM 377
378         // update pagination buttons
379         int maxHits = (Integer) maxHitsSelector.getSelectedItem();
380         next.setEnabled(entries.size() == maxHits);
381         prev.setEnabled(page > 0);
9119cf 382     }
JM 383
ee458f 384     protected FeedEntryModel getSelectedSyndicatedEntry() {
9119cf 385         int viewRow = table.getSelectedRow();
JM 386         int modelRow = table.convertRowIndexToModel(viewRow);
ee458f 387         FeedEntryModel entry = tableModel.get(modelRow);
9119cf 388         return entry;
JM 389     }
390
391     protected void viewCommit() {
ee458f 392         FeedEntryModel entry = getSelectedSyndicatedEntry();
9119cf 393         Utils.browse(entry.link);
JM 394     }
395
396     protected void viewCommitDiff() {
ee458f 397         FeedEntryModel entry = getSelectedSyndicatedEntry();
9119cf 398         Utils.browse(entry.link.replace("/commit/", "/commitdiff/"));
JM 399     }
400
401     protected void viewTree() {
ee458f 402         FeedEntryModel entry = getSelectedSyndicatedEntry();
9119cf 403         Utils.browse(entry.link.replace("/commit/", "/tree/"));
JM 404     }
405 }