PageRenderTime 48ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/projects/netbeans-7.3/git/src/org/netbeans/modules/git/ui/repository/RepositoryBrowserPanel.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 1186 lines | 1022 code | 111 blank | 53 comment | 209 complexity | a773ac3abaa1e6022934181bf072a090 MD5 | raw file
  1. /*
  2. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  3. *
  4. * Copyright 2010 Oracle and/or its affiliates. All rights reserved.
  5. *
  6. * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
  7. * Other names may be trademarks of their respective owners.
  8. *
  9. * The contents of this file are subject to the terms of either the GNU
  10. * General Public License Version 2 only ("GPL") or the Common
  11. * Development and Distribution License("CDDL") (collectively, the
  12. * "License"). You may not use this file except in compliance with the
  13. * License. You can obtain a copy of the License at
  14. * http://www.netbeans.org/cddl-gplv2.html
  15. * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
  16. * specific language governing permissions and limitations under the
  17. * License. When distributing the software, include this License Header
  18. * Notice in each file and include the License file at
  19. * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
  20. * particular file as subject to the "Classpath" exception as provided
  21. * by Oracle in the GPL Version 2 section of the License file that
  22. * accompanied this code. If applicable, add the following below the
  23. * License Header, with the fields enclosed by brackets [] replaced by
  24. * your own identifying information:
  25. * "Portions Copyrighted [year] [name of copyright owner]"
  26. *
  27. * If you wish your version of this file to be governed by only the CDDL
  28. * or only the GPL Version 2, indicate your decision by adding
  29. * "[Contributor] elects to include this software in this distribution
  30. * under the [CDDL or GPL Version 2] license." If you do not indicate a
  31. * single choice of license, a recipient has the option to distribute
  32. * your version of this file under either the CDDL, the GPL Version 2 or
  33. * to extend the choice of license to its licensees as provided above.
  34. * However, if you add GPL Version 2 code and therefore, elected the GPL
  35. * Version 2 license, then the option applies only if the new code is
  36. * made subject to such option by the copyright holder.
  37. *
  38. * Contributor(s):
  39. *
  40. * Portions Copyrighted 2010 Sun Microsystems, Inc.
  41. */
  42. package org.netbeans.modules.git.ui.repository;
  43. import java.awt.BorderLayout;
  44. import java.awt.Component;
  45. import java.awt.EventQueue;
  46. import java.awt.Image;
  47. import java.awt.KeyboardFocusManager;
  48. import java.awt.event.ActionEvent;
  49. import java.beans.BeanInfo;
  50. import java.beans.PropertyChangeEvent;
  51. import java.beans.PropertyChangeListener;
  52. import java.beans.PropertyVetoException;
  53. import java.io.File;
  54. import java.util.ArrayList;
  55. import java.util.Collections;
  56. import java.util.Comparator;
  57. import java.util.EnumSet;
  58. import java.util.HashMap;
  59. import java.util.LinkedList;
  60. import java.util.List;
  61. import java.util.Map;
  62. import java.util.Set;
  63. import java.util.TreeMap;
  64. import java.util.logging.Level;
  65. import java.util.logging.Logger;
  66. import javax.swing.AbstractAction;
  67. import javax.swing.Action;
  68. import javax.swing.Icon;
  69. import javax.swing.JPanel;
  70. import javax.swing.ScrollPaneConstants;
  71. import javax.swing.SwingUtilities;
  72. import javax.swing.UIManager;
  73. import javax.swing.event.ListSelectionEvent;
  74. import javax.swing.event.ListSelectionListener;
  75. import javax.swing.tree.TreeSelectionModel;
  76. import org.netbeans.libs.git.GitBranch;
  77. import org.netbeans.modules.git.client.GitClient;
  78. import org.netbeans.libs.git.GitException;
  79. import org.netbeans.libs.git.GitRemoteConfig;
  80. import org.netbeans.libs.git.GitRepositoryState;
  81. import org.netbeans.libs.git.GitRevisionInfo;
  82. import org.netbeans.libs.git.GitTag;
  83. import org.netbeans.libs.git.SearchCriteria;
  84. import org.netbeans.modules.git.Git;
  85. import org.netbeans.modules.git.GitRepositories;
  86. import org.netbeans.modules.git.client.GitProgressSupport;
  87. import org.netbeans.modules.git.ui.branch.CreateBranchAction;
  88. import org.netbeans.modules.git.ui.branch.DeleteBranchAction;
  89. import org.netbeans.modules.git.ui.checkout.CheckoutRevisionAction;
  90. import org.netbeans.modules.git.ui.fetch.FetchAction;
  91. import org.netbeans.modules.git.ui.merge.MergeRevisionAction;
  92. import org.netbeans.modules.git.ui.repository.remote.RemoveRemoteConfig;
  93. import org.netbeans.modules.git.ui.tag.CreateTagAction;
  94. import org.netbeans.modules.git.ui.tag.ManageTagsAction;
  95. import org.netbeans.modules.git.utils.GitUtils;
  96. import org.netbeans.modules.versioning.util.Utils;
  97. import org.openide.explorer.ExplorerManager;
  98. import org.openide.explorer.ExplorerManager.Provider;
  99. import org.openide.nodes.AbstractNode;
  100. import org.openide.nodes.Children;
  101. import org.openide.nodes.Node;
  102. import org.openide.util.ImageUtilities;
  103. import org.openide.util.Lookup;
  104. import org.openide.util.NbBundle;
  105. import org.openide.util.Parameters;
  106. import org.openide.util.RequestProcessor;
  107. import org.openide.util.WeakListeners;
  108. import org.openide.util.actions.SystemAction;
  109. import org.openide.util.lookup.Lookups;
  110. import org.openide.util.lookup.ProxyLookup;
  111. import org.openide.windows.TopComponent;
  112. /**
  113. *
  114. * @author ondra
  115. */
  116. public class RepositoryBrowserPanel extends JPanel implements Provider, PropertyChangeListener, ListSelectionListener {
  117. AbstractNode root;
  118. private static final RequestProcessor RP = new RequestProcessor("RepositoryPanel", 1); //NOI18N
  119. private static final Logger LOG = Logger.getLogger(RepositoryBrowserPanel.class.getName());
  120. private final ExplorerManager manager;
  121. private final EnumSet<Option> options;
  122. private Revision currRevision;
  123. private File currRepository;
  124. public static final String PROP_REVISION_CHANGED = "RepositoryBrowserPanel.revision"; //NOI18N
  125. private final File[] roots;
  126. public static enum Option {
  127. DISPLAY_ALL_REPOSITORIES,
  128. DISPLAY_BRANCHES_LOCAL,
  129. DISPLAY_BRANCHES_REMOTE,
  130. DISPLAY_COMMIT_IDS,
  131. DISPLAY_REMOTES,
  132. DISPLAY_REVISIONS,
  133. DISPLAY_TAGS,
  134. DISPLAY_TOOLBAR,
  135. ENABLE_POPUP
  136. }
  137. public static final EnumSet<Option> OPTIONS_INSIDE_PANEL = EnumSet.of(Option.DISPLAY_BRANCHES_LOCAL,
  138. Option.DISPLAY_BRANCHES_REMOTE,
  139. Option.DISPLAY_REVISIONS,
  140. Option.DISPLAY_TAGS);
  141. public RepositoryBrowserPanel () {
  142. this(EnumSet.complementOf(EnumSet.of(Option.DISPLAY_REVISIONS)), null, new File[0], null);
  143. }
  144. public RepositoryBrowserPanel (EnumSet<Option> options, File repository, File[] roots, RepositoryInfo info) {
  145. Parameters.notNull("roots", roots);
  146. this.currRepository = repository;
  147. this.root = options.contains(Option.DISPLAY_ALL_REPOSITORIES) ? new AbstractNode(new RepositoriesChildren()) : new RepositoryNode(repository, info);
  148. this.manager = new ExplorerManager();
  149. this.options = options;
  150. this.roots = roots;
  151. initComponents();
  152. if (!options.contains(Option.DISPLAY_TOOLBAR)) {
  153. toolbar.setVisible(false);
  154. }
  155. tree.setRootVisible(false);
  156. tree.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
  157. if (!options.contains(Option.DISPLAY_REVISIONS)) {
  158. remove(jSplitPane1);
  159. add(tree, BorderLayout.CENTER);
  160. }
  161. }
  162. @Override
  163. public ExplorerManager getExplorerManager () {
  164. return manager;
  165. }
  166. @Override
  167. public void addNotify () {
  168. super.addNotify();
  169. getExplorerManager().setRootContext(root);
  170. getExplorerManager().addPropertyChangeListener(this);
  171. if (toolbar.isVisible()) {
  172. attachToolbarListeners();
  173. }
  174. revisionsPanel1.lstRevisions.addListSelectionListener(this);
  175. if (options.contains(Option.DISPLAY_REVISIONS)) {
  176. KeyboardFocusManager.getCurrentKeyboardFocusManager().addPropertyChangeListener(this);
  177. revisionsPanel1.updateHistory(currRepository, roots, currRevision);
  178. EventQueue.invokeLater(new Runnable() {
  179. @Override
  180. public void run() {
  181. jSplitPane1.setDividerLocation(0.5);
  182. }
  183. });
  184. }
  185. }
  186. @Override
  187. public void removeNotify() {
  188. if (options.contains(Option.DISPLAY_REVISIONS)) {
  189. KeyboardFocusManager.getCurrentKeyboardFocusManager().removePropertyChangeListener(this);
  190. }
  191. revisionsPanel1.lstRevisions.removeListSelectionListener(this);
  192. getExplorerManager().removePropertyChangeListener(this);
  193. if (toolbar.isVisible()) {
  194. detachToolbarListeners();
  195. }
  196. super.removeNotify();
  197. }
  198. @Override
  199. public void propertyChange (PropertyChangeEvent evt) {
  200. if (evt.getPropertyName() == ExplorerManager.PROP_SELECTED_NODES) {
  201. TopComponent tc = (TopComponent) SwingUtilities.getAncestorOfClass(TopComponent.class, this);
  202. if (tc != null) {
  203. tc.setActivatedNodes(getExplorerManager().getSelectedNodes());
  204. }
  205. currRepository = null;
  206. Revision oldRevision = currRevision;
  207. currRevision = null;
  208. if (getExplorerManager().getSelectedNodes().length == 1) {
  209. Node selectedNode = getExplorerManager().getSelectedNodes()[0];
  210. currRevision = selectedNode.getLookup().lookup(Revision.class);
  211. currRepository = lookupRepository(selectedNode);
  212. }
  213. if ((currRevision != null || oldRevision != null) && !(currRevision != null && oldRevision != null
  214. && currRevision.getName().equals(oldRevision.getName()) && currRevision.getRevision().equals(oldRevision.getRevision()))) {
  215. firePropertyChange(PROP_REVISION_CHANGED, oldRevision, currRevision);
  216. }
  217. if (options.contains(Option.DISPLAY_REVISIONS) && currRevision != null) {
  218. revisionsPanel1.updateHistory(currRepository, roots, currRevision);
  219. }
  220. } else if (options.contains(Option.DISPLAY_REVISIONS) && "focusOwner".equals(evt.getPropertyName())) {
  221. Component compNew = (Component) evt.getNewValue();
  222. if (compNew != null) {
  223. if (SwingUtilities.getAncestorOfClass(tree.getClass(), compNew) != null) {
  224. if (getExplorerManager().getSelectedNodes().length == 1) {
  225. propertyChange(new PropertyChangeEvent(tree, ExplorerManager.PROP_SELECTED_NODES, getExplorerManager().getSelectedNodes(), getExplorerManager().getSelectedNodes()));
  226. }
  227. } else if (revisionsPanel1.lstRevisions == compNew) {
  228. int selection = revisionsPanel1.lstRevisions.getSelectedIndex();
  229. if (selection != -1) {
  230. valueChanged(new ListSelectionEvent(revisionsPanel1.lstRevisions, selection, selection, false));
  231. }
  232. }
  233. }
  234. }
  235. }
  236. private File lookupRepository (Node selectedNode) {
  237. // there should ALWAYS be a repository node somewhere in the root
  238. while (!(selectedNode instanceof RepositoryNode) && selectedNode != null) {
  239. selectedNode = selectedNode.getParentNode();
  240. }
  241. return selectedNode == null ? null : ((RepositoryNode) selectedNode).getRepository();
  242. }
  243. public void selectRepository (File repository) {
  244. Node[] nodes = root.getChildren().getNodes();
  245. for (Node node : nodes) {
  246. if (node instanceof RepositoryNode && repository.equals(node.getLookup().lookup(File.class))) {
  247. tree.expandNode(node);
  248. try {
  249. getExplorerManager().setSelectedNodes(new Node[] { node });
  250. } catch (PropertyVetoException ex) {
  251. }
  252. break;
  253. }
  254. }
  255. }
  256. private void attachToolbarListeners () {
  257. }
  258. private void detachToolbarListeners () {
  259. }
  260. @Override
  261. public void valueChanged (ListSelectionEvent e) {
  262. if (!e.getValueIsAdjusting() && revisionsPanel1.lstRevisions.isFocusOwner()) {
  263. GitRevisionInfo selectedRevision = revisionsPanel1.getSelectedRevision();
  264. Revision oldRevision = currRevision;
  265. if (selectedRevision == null && currRevision != null) {
  266. currRevision = null;
  267. firePropertyChange(PROP_REVISION_CHANGED, oldRevision, currRevision);
  268. } else if (selectedRevision != null) {
  269. currRevision = new Revision(selectedRevision.getRevision(), selectedRevision.getRevision());
  270. if (oldRevision == null || !currRevision.getName().equals(oldRevision.getName())
  271. || !currRevision.getRevision().equals(oldRevision.getRevision())) {
  272. firePropertyChange(PROP_REVISION_CHANGED, oldRevision, currRevision);
  273. }
  274. }
  275. }
  276. }
  277. private static final HashMap<String, Image> cachedIcons = new HashMap<String, Image>(2);
  278. private abstract class RepositoryBrowserNode extends AbstractNode {
  279. protected RepositoryBrowserNode (Children children, File repository) {
  280. this(children, repository, null);
  281. }
  282. protected RepositoryBrowserNode (Children children, File repository, Lookup lookup) {
  283. super(children, lookup == null ? Lookups.singleton(repository) : new ProxyLookup(Lookups.singleton(repository), lookup));
  284. }
  285. @Override
  286. public final Action[] getActions (boolean context) {
  287. return options.contains(Option.ENABLE_POPUP) ? getPopupActions(context) : RepositoryBrowserNode.this.getPopupActions(context);
  288. }
  289. protected Action[] getPopupActions (boolean context) {
  290. return new Action[0];
  291. }
  292. protected Image getFolderIcon (int type) {
  293. Image img = null;
  294. if (type == BeanInfo.ICON_COLOR_16x16) {
  295. img = findIcon("Nb.Explorer.Folder.icon", "Tree.closedIcon"); //NOI18N
  296. }
  297. if (img == null) {
  298. img = super.getIcon(type);
  299. }
  300. return img;
  301. }
  302. protected Image getOpenedFolderIcon (int type) {
  303. Image img = null;
  304. if (type == BeanInfo.ICON_COLOR_16x16) {
  305. img = findIcon("Nb.Explorer.Folder.openedIcon", "Tree.openIcon"); //NOI18N
  306. }
  307. if (img == null) {
  308. img = super.getOpenedIcon(type);
  309. }
  310. return img;
  311. }
  312. private Image findIcon (String key1, String key2) {
  313. Image img = cachedIcons.containsKey(key1) ? cachedIcons.get(key1) : null;
  314. if (img == null) {
  315. img = findIcon(key1);
  316. if (img == null) {
  317. img = findIcon(key2);
  318. }
  319. cachedIcons.put(key1, img);
  320. }
  321. return img;
  322. }
  323. private Image findIcon (String key) {
  324. Object obj = UIManager.get(key);
  325. if (obj instanceof Image) {
  326. return (Image)obj;
  327. }
  328. if (obj instanceof Icon) {
  329. Icon icon = (Icon)obj;
  330. return ImageUtilities.icon2Image(icon);
  331. }
  332. return null;
  333. }
  334. }
  335. private class RepositoriesChildren extends Children.SortedMap<File> {
  336. private final PropertyChangeListener list;
  337. private boolean initialized = false;
  338. public RepositoriesChildren () {
  339. setComparator(new Comparator<Node>() {
  340. @Override
  341. public int compare (Node o1, Node o2) {
  342. int result;
  343. if (o1 instanceof RepositoryNode && o2 instanceof RepositoryNode) {
  344. File repo1 = ((RepositoryNode) o1).getRepository();
  345. File repo2 = ((RepositoryNode) o2).getRepository();
  346. result = repo1.getName().compareTo(repo2.getName());
  347. if (result == 0 && !repo1.equals(repo2)) {
  348. result = repo1.getAbsolutePath().compareTo(repo2.getAbsolutePath());
  349. }
  350. } else {
  351. result = o1.toString().compareTo(o2.toString());
  352. }
  353. return result;
  354. }
  355. });
  356. GitRepositories.getInstance().addPropertyChangeListener(WeakListeners.propertyChange(list = new PropertyChangeListener() {
  357. @Override
  358. @SuppressWarnings("unchecked")
  359. public void propertyChange (PropertyChangeEvent evt) {
  360. final Set<File> oldValues = (Set<File>) evt.getOldValue();
  361. final Set<File> newValues = (Set<File>) evt.getNewValue();
  362. if (oldValues.size() > newValues.size()) {
  363. oldValues.removeAll(newValues);
  364. removeAll(oldValues);
  365. } else if (oldValues.size() < newValues.size()) {
  366. newValues.removeAll(oldValues);
  367. RP.post(new Runnable () {
  368. @Override
  369. public void run () {
  370. java.util.Map<File, RepositoryNode> nodes = new HashMap<File, RepositoryNode>();
  371. for (File r : newValues) {
  372. RepositoryInfo info = RepositoryInfo.getInstance(r);
  373. if (info == null) {
  374. LOG.log(Level.INFO, "RepositoriesChildren.propertyChange() : Null info for {0}", r); //NOI18N
  375. } else {
  376. nodes.put(r, new RepositoryNode(r, info));
  377. }
  378. }
  379. putAll(nodes);
  380. }
  381. });
  382. }
  383. }
  384. }, GitRepositories.getInstance()));
  385. }
  386. @Override
  387. protected void addNotify() {
  388. super.addNotify();
  389. if (!initialized) {
  390. // initialize keys
  391. initialized = true;
  392. list.propertyChange(new PropertyChangeEvent(GitRepositories.getInstance(), GitRepositories.PROP_REPOSITORIES, Collections.<File>emptySet(), GitRepositories.getInstance().getKnownRepositories()));
  393. }
  394. }
  395. }
  396. private class RepositoryNode extends RepositoryBrowserNode implements PropertyChangeListener {
  397. private PropertyChangeListener list;
  398. private final File repository;
  399. public RepositoryNode (final File repository, RepositoryInfo info) {
  400. super(new RepositoryChildren(), repository);
  401. this.repository = repository;
  402. setIconBaseWithExtension("org/netbeans/modules/git/resources/icons/repository.png"); //NOI18N
  403. if (info == null) {
  404. setDisplayName(repository.getName());
  405. RP.post(new Runnable () {
  406. @Override
  407. public void run () {
  408. RepositoryInfo info = RepositoryInfo.getInstance(repository);
  409. if (info == null) {
  410. LOG.log(Level.INFO, "RepositoryNode() : Null info for {0}", repository); //NOI18N
  411. } else {
  412. setName(info);
  413. info.addPropertyChangeListener(list = WeakListeners.propertyChange(RepositoryNode.this, info));
  414. }
  415. }
  416. });
  417. } else {
  418. setName(info);
  419. info.addPropertyChangeListener(list = WeakListeners.propertyChange(this, info));
  420. }
  421. }
  422. private void setName (RepositoryInfo info) {
  423. String annotation;
  424. String branchLabel = ""; //NOI18N
  425. GitBranch branch = info.getActiveBranch();
  426. if (branch != null) {
  427. branchLabel = branch.getName();
  428. if (branchLabel == GitBranch.NO_BRANCH) { // do not use equals
  429. Map<String, GitTag> tags = info.getTags();
  430. StringBuilder tagLabel = new StringBuilder(); //NOI18N
  431. for (GitTag tag : tags.values()) {
  432. if (tag.getTaggedObjectId().equals(branch.getId())) {
  433. tagLabel.append(",").append(tag.getTagName());
  434. }
  435. }
  436. if (tagLabel.length() <= 1) {
  437. // not on a branch or tag, show at least part of commit id
  438. branchLabel = branch.getId();
  439. if (branchLabel.length() > 7) {
  440. branchLabel = branchLabel.substring(0, 7) + "..."; //NOI18N
  441. }
  442. } else {
  443. tagLabel.delete(0, 1);
  444. branchLabel = tagLabel.toString();
  445. }
  446. }
  447. }
  448. GitRepositoryState repositoryState = info.getRepositoryState();
  449. if (repositoryState != GitRepositoryState.SAFE) {
  450. annotation = repositoryState.toString() + " - " + branchLabel; //NOI18N
  451. } else {
  452. annotation = branchLabel;
  453. }
  454. setDisplayName(info.getName() + " [" + annotation + "]");
  455. }
  456. @Override
  457. public void propertyChange (PropertyChangeEvent evt) {
  458. if (evt.getSource() instanceof RepositoryInfo) {
  459. setName((RepositoryInfo) evt.getSource());
  460. }
  461. }
  462. @Override
  463. public String toString() {
  464. return getDisplayName();
  465. }
  466. public File getRepository() {
  467. return repository;
  468. }
  469. @Override
  470. protected Action[] getPopupActions (boolean context) {
  471. return new Action[] {
  472. SystemAction.get(FetchAction.class)
  473. };
  474. }
  475. @Override
  476. public String getShortDescription () {
  477. return repository.getAbsolutePath();
  478. }
  479. }
  480. private class RepositoryChildren extends Children.Keys<AbstractNode> {
  481. boolean initialized = false;
  482. @Override
  483. protected void addNotify () {
  484. super.addNotify();
  485. if (!initialized) {
  486. initialized = true;
  487. List<AbstractNode> keys = new LinkedList<AbstractNode>();
  488. if (options.contains(Option.DISPLAY_BRANCHES_LOCAL) || options.contains(Option.DISPLAY_BRANCHES_REMOTE)) {
  489. keys.add(new BranchesTopNode(((RepositoryNode) getNode()).getRepository()));
  490. }
  491. if (options.contains(Option.DISPLAY_TAGS)) {
  492. keys.add(new TagsNode(((RepositoryNode) getNode()).getRepository()));
  493. }
  494. if (options.contains(Option.DISPLAY_REMOTES)) {
  495. keys.add(new RemotesNode(((RepositoryNode) getNode()).getRepository()));
  496. }
  497. setKeys(keys);
  498. }
  499. }
  500. @Override
  501. protected void removeNotify () {
  502. setKeys(Collections.<AbstractNode>emptySet());
  503. super.removeNotify();
  504. }
  505. @Override
  506. protected Node[] createNodes (AbstractNode key) {
  507. return new Node[] { key };
  508. }
  509. }
  510. //<editor-fold defaultstate="collapsed" desc="branches">
  511. private class BranchesTopNode extends RepositoryBrowserNode {
  512. public BranchesTopNode (File repository) {
  513. super(new BranchesTopChildren(repository), repository);
  514. setIconBaseWithExtension("org/netbeans/modules/git/resources/icons/branches.png"); //NOI18N
  515. }
  516. @Override
  517. public String getDisplayName () {
  518. return getName();
  519. }
  520. @Override
  521. public String getName () {
  522. return NbBundle.getMessage(RepositoryBrowserPanel.class, "LBL_RepositoryPanel.BranchesNode.name"); //NOI18N
  523. }
  524. @Override
  525. public Action[] getPopupActions (boolean context) {
  526. return new Action[] {
  527. new AbstractAction(NbBundle.getMessage(BranchesTopNode.class, "LBL_RepositoryPanel.RefreshBranchesAction.name")) { //NOI18N
  528. @Override
  529. public void actionPerformed (ActionEvent e) {
  530. ((BranchesTopChildren) getChildren()).refreshBranches();
  531. }
  532. }
  533. };
  534. }
  535. }
  536. private static enum BranchNodeType {
  537. LOCAL {
  538. @Override
  539. public String toString () {
  540. return NbBundle.getMessage(RepositoryBrowserPanel.class, "LBL_RepositoryPanel.BranchesChildren.LocalNode.name"); //NOI18N
  541. }
  542. },
  543. REMOTE {
  544. @Override
  545. public String toString () {
  546. return NbBundle.getMessage(RepositoryBrowserPanel.class, "LBL_RepositoryPanel.BranchesChildren.RemoteNode.name"); //NOI18N
  547. }
  548. }
  549. }
  550. private class BranchesTopChildren extends Children.Keys<BranchNodeType> implements PropertyChangeListener {
  551. private final File repository;
  552. private java.util.Map<String, GitBranch> branches = new TreeMap<String, GitBranch>();
  553. private BranchesNode local, remote;
  554. private BranchesTopChildren (File repository) {
  555. this.repository = repository;
  556. RepositoryInfo info = RepositoryInfo.getInstance(repository);
  557. if (info == null) {
  558. LOG.log(Level.INFO, "BranchesTopChildren() : Null info for {0}", repository); //NOI18N
  559. } else {
  560. info.addPropertyChangeListener(WeakListeners.propertyChange(this, info));
  561. }
  562. }
  563. @Override
  564. protected void addNotify () {
  565. super.addNotify();
  566. List<BranchNodeType> keys = new LinkedList<BranchNodeType>();
  567. if (options.contains(Option.DISPLAY_BRANCHES_LOCAL)) {
  568. keys.add(BranchNodeType.LOCAL);
  569. }
  570. if (options.contains(Option.DISPLAY_BRANCHES_REMOTE)) {
  571. keys.add(BranchNodeType.REMOTE);
  572. }
  573. setKeys(keys);
  574. refreshBranches();
  575. }
  576. @Override
  577. protected void removeNotify () {
  578. setKeys(Collections.<BranchNodeType>emptySet());
  579. super.removeNotify();
  580. }
  581. @Override
  582. protected Node[] createNodes (BranchNodeType key) {
  583. BranchesNode node;
  584. switch (key) {
  585. case LOCAL:
  586. node = local = new BranchesNode(repository, key, branches);
  587. break;
  588. case REMOTE:
  589. node = remote = new BranchesNode(repository, key, branches);
  590. break;
  591. default:
  592. throw new IllegalStateException();
  593. }
  594. return new Node[] { node };
  595. }
  596. private void refreshBranches () {
  597. new GitProgressSupport.NoOutputLogging() {
  598. @Override
  599. protected void perform () {
  600. RepositoryInfo info = RepositoryInfo.getInstance(repository);
  601. info.refresh();
  602. java.util.Map<String, GitBranch> branches = info.getBranches();
  603. if (!isCanceled()) {
  604. refreshBranches(branches);
  605. }
  606. }
  607. }.start(RP, repository, NbBundle.getMessage(BranchesTopChildren.class, "MSG_RepositoryPanel.refreshingBranches")); //NOI18N
  608. }
  609. private void refreshBranches (java.util.Map<String, GitBranch> branches) {
  610. if (branches.isEmpty()) {
  611. BranchesTopChildren.this.branches.clear();
  612. } else {
  613. branches = new java.util.HashMap<String, GitBranch>(branches);
  614. BranchesTopChildren.this.branches.keySet().retainAll(branches.keySet());
  615. for (java.util.Map.Entry<String, GitBranch> e : BranchesTopChildren.this.branches.entrySet()) {
  616. GitBranch newBranchInfo = branches.get(e.getKey());
  617. // do not refresh branches that don't change their active state or head id
  618. if (newBranchInfo != null && (newBranchInfo.getId().equals(e.getValue().getId()) && newBranchInfo.isActive() == e.getValue().isActive())) {
  619. branches.remove(e.getKey());
  620. }
  621. }
  622. BranchesTopChildren.this.branches.putAll(branches);
  623. }
  624. if (local != null) {
  625. local.refresh();
  626. }
  627. if (remote != null) {
  628. remote.refresh();
  629. }
  630. }
  631. @Override
  632. public void propertyChange (final PropertyChangeEvent evt) {
  633. if (RepositoryInfo.PROPERTY_BRANCHES.equals(evt.getPropertyName())) {
  634. RP.post(new Runnable() {
  635. @Override
  636. @SuppressWarnings("unchecked")
  637. public void run () {
  638. refreshBranches((java.util.Map<String, GitBranch>) evt.getNewValue());
  639. }
  640. });
  641. }
  642. }
  643. }
  644. private class BranchesNode extends RepositoryBrowserNode {
  645. private final BranchNodeType type;
  646. private BranchesNode (File repository, BranchNodeType type, Map<String, GitBranch> branches) {
  647. super(new BranchesChildren(type, branches), repository);
  648. this.type = type;
  649. }
  650. private void refresh () {
  651. ((BranchesChildren) getChildren()).refreshKeys();
  652. }
  653. @Override
  654. public String getName () {
  655. return type.toString();
  656. }
  657. @Override
  658. public String getDisplayName () {
  659. return getName();
  660. }
  661. @Override
  662. public Image getIcon (int type) {
  663. return getFolderIcon(type);
  664. }
  665. @Override
  666. public Image getOpenedIcon (int type) {
  667. return getOpenedFolderIcon(type);
  668. }
  669. }
  670. private class BranchesChildren extends Children.Keys<GitBranch> {
  671. private final BranchNodeType type;
  672. private final java.util.Map<String, GitBranch> branches;
  673. private BranchesChildren (BranchNodeType type, java.util.Map<String, GitBranch> branches) {
  674. this.type = type;
  675. this.branches = branches;
  676. }
  677. @Override
  678. protected void addNotify () {
  679. super.addNotify();
  680. RP.post(new Runnable () {
  681. @Override
  682. public void run () {
  683. refreshKeys();
  684. }
  685. });
  686. }
  687. @Override
  688. protected Node[] createNodes (GitBranch key) {
  689. Node node = getNode();
  690. while (!(node instanceof RepositoryNode)) {
  691. node = node.getParentNode();
  692. }
  693. File repository = ((RepositoryNode) node).getRepository();
  694. return new Node[] { new BranchNode(repository, key) };
  695. }
  696. private void refreshKeys () {
  697. List<GitBranch> keys = new LinkedList<GitBranch>();
  698. for (java.util.Map.Entry<String, GitBranch> e : branches.entrySet()) {
  699. GitBranch branch = e.getValue();
  700. if (type == BranchNodeType.REMOTE && branch.isRemote() || type == BranchNodeType.LOCAL && !branch.isRemote()) {
  701. keys.add(branch);
  702. }
  703. }
  704. setKeys(keys);
  705. }
  706. }
  707. private class BranchNode extends RepositoryBrowserNode {
  708. private PropertyChangeListener list;
  709. private boolean active;
  710. private final String branchName;
  711. private String branchId;
  712. private final GitBranch trackedBranch;
  713. private String lastTrackingMyId;
  714. private String lastTrackingOtherId;
  715. public BranchNode (File repository, GitBranch branch) {
  716. super(Children.LEAF, repository, Lookups.singleton(new Revision(branch.getId(), branch.getName())));
  717. branchName = branch.getName();
  718. branchId = branch.getId();
  719. trackedBranch = branch.getTrackedBranch();
  720. setIconBaseWithExtension("org/netbeans/modules/git/resources/icons/branch.png"); //NOI18N
  721. RepositoryInfo info = RepositoryInfo.getInstance(repository);
  722. if (info == null) {
  723. LOG.log(Level.INFO, "BranchNode() : Null info for {0}", repository); //NOI18N
  724. } else {
  725. info.addPropertyChangeListener(WeakListeners.propertyChange(list = new PropertyChangeListener() {
  726. @Override
  727. public void propertyChange (PropertyChangeEvent evt) {
  728. if (RepositoryInfo.PROPERTY_ACTIVE_BRANCH.equals(evt.getPropertyName()) || RepositoryInfo.PROPERTY_HEAD.equals(evt.getPropertyName())) {
  729. refreshActiveBranch((GitBranch) evt.getNewValue());
  730. }
  731. }
  732. }, info));
  733. refreshActiveBranch(info.getActiveBranch());
  734. }
  735. refreshTracking(branch.getTrackedBranch(), repository);
  736. }
  737. @Override
  738. public String getDisplayName () {
  739. return getName(false);
  740. }
  741. @Override
  742. public String getHtmlDisplayName() {
  743. return getName(true);
  744. }
  745. @Override
  746. public String getName() {
  747. return getName(false);
  748. }
  749. public String getName (boolean html) {
  750. StringBuilder sb = new StringBuilder();
  751. if (active && html) {
  752. sb.append("<html><strong>").append(branchName).append("</strong>"); //NOI18N
  753. } else {
  754. sb.append(branchName);
  755. }
  756. if (options.contains(Option.DISPLAY_COMMIT_IDS)) {
  757. if (trackedBranch != null) {
  758. sb.append(NbBundle.getMessage(RepositoryBrowserPanel.class, "LBL_BranchNode.basedOn", trackedBranch.getName())); //NOI18N
  759. }
  760. sb.append(" - ").append(branchId); //NOI18N
  761. }
  762. return sb.toString();
  763. }
  764. private void refreshActiveBranch (GitBranch activeBranch) {
  765. String oldHtmlName = getHtmlDisplayName();
  766. boolean oldActive = active;
  767. if (activeBranch.getName().equals(branchName)) {
  768. active = true;
  769. this.branchId = activeBranch.getId();
  770. refreshTracking(activeBranch.getTrackedBranch(), lookupRepository(this));
  771. } else {
  772. active = false;
  773. }
  774. if (active != oldActive) {
  775. setIconBaseWithExtension("org/netbeans/modules/git/resources/icons/" + (active ? "active_branch" : "branch") + ".png"); //NOI18N
  776. }
  777. String newHtmlName = getHtmlDisplayName();
  778. if (!oldHtmlName.equals(newHtmlName)) {
  779. fireDisplayNameChange(null, null);
  780. }
  781. }
  782. @Override
  783. protected Action[] getPopupActions (boolean context) {
  784. List<Action> actions = new LinkedList<Action>();
  785. if (currRepository != null && branchName != null) {
  786. final File repo = currRepository;
  787. final String branch = branchName;
  788. actions.add(new AbstractAction(NbBundle.getMessage(CheckoutRevisionAction.class, "LBL_CheckoutRevisionAction_PopupName")) { //NOI18N
  789. @Override
  790. public void actionPerformed (ActionEvent e) {
  791. Utils.postParallel(new Runnable () {
  792. @Override
  793. public void run() {
  794. CheckoutRevisionAction action = SystemAction.get(CheckoutRevisionAction.class);
  795. action.checkoutRevision(repo, branch);
  796. }
  797. }, 0);
  798. }
  799. });
  800. actions.add(new AbstractAction(NbBundle.getMessage(CreateBranchAction.class, "LBL_CreateBranchAction_PopupName")) { //NOI18N
  801. @Override
  802. public void actionPerformed (ActionEvent e) {
  803. Utils.postParallel(new Runnable () {
  804. @Override
  805. public void run() {
  806. CreateBranchAction action = SystemAction.get(CreateBranchAction.class);
  807. action.createBranch(repo, branch);
  808. }
  809. }, 0);
  810. }
  811. });
  812. actions.add(new AbstractAction(NbBundle.getMessage(CreateTagAction.class, "LBL_CreateTagAction_PopupName")) { //NOI18N
  813. @Override
  814. public void actionPerformed (ActionEvent e) {
  815. CreateTagAction action = SystemAction.get(CreateTagAction.class);
  816. action.createTag(repo, branch);
  817. }
  818. });
  819. actions.add(new AbstractAction(NbBundle.getMessage(MergeRevisionAction.class, "LBL_MergeRevisionAction_PopupName")) { //NOI18N
  820. @Override
  821. public void actionPerformed (ActionEvent e) {
  822. Utils.postParallel(new Runnable () {
  823. @Override
  824. public void run() {
  825. MergeRevisionAction action = SystemAction.get(MergeRevisionAction.class);
  826. action.mergeRevision(repo, branch);
  827. }
  828. }, 0);
  829. }
  830. @Override
  831. public boolean isEnabled() {
  832. return !active;
  833. }
  834. });
  835. actions.add(new AbstractAction(NbBundle.getMessage(DeleteBranchAction.class, "LBL_DeleteBranchAction_PopupName")) { //NOI18N
  836. @Override
  837. public void actionPerformed (ActionEvent e) {
  838. DeleteBranchAction action = SystemAction.get(DeleteBranchAction.class);
  839. action.deleteBranch(currRepository, branchName);
  840. }
  841. @Override
  842. public boolean isEnabled() {
  843. return !active;
  844. }
  845. });
  846. }
  847. return actions.toArray(new Action[actions.size()]);
  848. }
  849. private void refreshTracking (final GitBranch trackedBranch, final File repository) {
  850. if (trackedBranch != null && repository != null && options.contains(Option.DISPLAY_COMMIT_IDS)
  851. && (!branchId.equals(lastTrackingMyId) || !trackedBranch.getId().equals(lastTrackingOtherId))) {
  852. lastTrackingMyId = branchId;
  853. lastTrackingOtherId = trackedBranch.getId();
  854. if (trackedBranch.getId().equals(branchId)) {
  855. setShortDescription(NbBundle.getMessage(RepositoryBrowserPanel.class, "MSG_BranchNode.tracking.inSync", trackedBranch.getName())); //NOI18N
  856. } else {
  857. final String id = branchId;
  858. RP.post(new Runnable() {
  859. @Override
  860. public void run () {
  861. String tt = null;
  862. GitClient client = null;
  863. try {
  864. client = Git.getInstance().getClient(repository);
  865. GitRevisionInfo info = client.getCommonAncestor(new String[] { id, trackedBranch.getId() }, GitUtils.NULL_PROGRESS_MONITOR);
  866. if (info == null || !(info.getRevision().equals(id) || info.getRevision().equals(trackedBranch.getId()))) {
  867. tt = NbBundle.getMessage(RepositoryBrowserPanel.class, "MSG_BranchNode.tracking.mergeNeeded", trackedBranch.getName()); //NOI18N
  868. } else {
  869. SearchCriteria crit = new SearchCriteria();
  870. if (info.getRevision().equals(trackedBranch.getId())) {
  871. crit.setRevisionFrom(trackedBranch.getId());
  872. crit.setRevisionTo(id);
  873. } else if (info.getRevision().equals(id)) {
  874. crit.setRevisionFrom(id);
  875. crit.setRevisionTo(trackedBranch.getId());
  876. }
  877. GitRevisionInfo[] revs = client.log(crit, GitUtils.NULL_PROGRESS_MONITOR);
  878. int diff = (revs.length - 1);
  879. if (info.getRevision().equals(trackedBranch.getId())) {
  880. tt = NbBundle.getMessage(RepositoryBrowserPanel.class, diff == 1
  881. ? "MSG_BranchNode.tracking.ahead.commit" : "MSG_BranchNode.tracking.ahead.commits", //NOI18N
  882. trackedBranch.getName(), diff);
  883. } else if (info.getRevision().equals(id)) {
  884. tt = NbBundle.getMessage(RepositoryBrowserPanel.class, diff == 1
  885. ? "MSG_BranchNode.tracking.behind.commit" : "MSG_BranchNode.tracking.behind.commits", //NOI18N
  886. trackedBranch.getName(), diff);
  887. }
  888. }
  889. } catch (GitException ex) {
  890. LOG.log(Level.INFO, null, ex);
  891. } finally {
  892. if (client != null) {
  893. client.release();
  894. }
  895. }
  896. final String toolTip = tt;
  897. EventQueue.invokeLater(new Runnable() {
  898. @Override
  899. public void run () {
  900. setShortDescription(toolTip);
  901. }
  902. });
  903. }
  904. });
  905. }
  906. }
  907. }
  908. }
  909. //</editor-fold>
  910. //<editor-fold defaultstate="collapsed" desc="tags">
  911. private class TagsNode extends RepositoryBrowserNode {
  912. public TagsNode (File repository) {
  913. super(new TagChildren(repository), repository);
  914. assert repository != null;
  915. setIconBaseWithExtension("org/netbeans/modules/git/resources/icons/tags.png"); //NOI18N
  916. }
  917. @Override
  918. public String getDisplayName () {
  919. return getName();
  920. }
  921. @Override
  922. public String getName () {
  923. return NbBundle.getMessage(RepositoryBrowserPanel.class, "LBL_RepositoryPanel.TagsNode.name"); //NOI18N
  924. }
  925. @Override
  926. public Action[] getPopupActions (boolean context) {
  927. return new Action[] {
  928. new AbstractAction(NbBundle.getMessage(BranchesTopNode.class, "LBL_RepositoryPanel.RefreshTagsAction.name")) { //NOI18N
  929. @Override
  930. public void actionPerformed (ActionEvent e) {
  931. ((TagChildren) getChildren()).refreshTags();
  932. }
  933. }
  934. };
  935. }
  936. }
  937. private class TagChildren extends Children.Keys<GitTag> implements PropertyChangeListener {
  938. private final java.util.Map<String, GitTag> tags = new TreeMap<String, GitTag>();
  939. private final File repository;
  940. private TagChildren (File repository) {
  941. this.repository = repository;
  942. RepositoryInfo info = RepositoryInfo.getInstance(repository);
  943. if (info == null) {
  944. LOG.log(Level.INFO, "TagChildren() : Null info for {0}", repository); //NOI18N
  945. } else {
  946. info.addPropertyChangeListener(WeakListeners.propertyChange(this, info));
  947. }
  948. }
  949. @Override
  950. protected void addNotify () {
  951. super.addNotify();
  952. refreshTags();
  953. }
  954. @Override
  955. protected void removeNotify () {
  956. setKeys(Collections.<GitTag>emptySet());
  957. super.removeNotify();
  958. }
  959. @Override
  960. protected Node[] createNodes (GitTag key) {
  961. return new Node[] { new TagNode(repository, key) };
  962. }
  963. private void refreshTags () {
  964. new GitProgressSupport.NoOutputLogging() {
  965. @Override
  966. protected void perform () {
  967. RepositoryInfo info = RepositoryInfo.getInstance(repository);
  968. info.refresh();
  969. java.util.Map<String, GitTag> tags = info.getTags();
  970. if (!isCanceled()) {
  971. refreshTags(tags);
  972. }
  973. }
  974. }.start(RP, repository, NbBundle.getMessage(BranchesTopChildren.class, "MSG_RepositoryPanel.refreshingTags")); //NOI18N
  975. }
  976. private void refreshTags (java.util.Map<String, GitTag> tags) {
  977. if (tags.isEmpty()) {
  978. this.tags.clear();
  979. } else {
  980. tags = new java.util.HashMap<String, GitTag>(tags);
  981. this.tags.keySet().retainAll(tags.keySet());
  982. for (java.util.Map.Entry<String, GitTag> e : this.tags.entrySet()) {
  983. GitTag newTagInfo = tags.get(e.getKey());
  984. // do not refresh tags they keep the same
  985. if (newTagInfo != null && (newTagInfo.getTaggedObjectId().equals(e.getValue().getTaggedObjectId())
  986. && newTagInfo.getMessage().equals(e.getValue().getMessage()))) {
  987. tags.remove(e.getKey());
  988. }
  989. }
  990. this.tags.putAll(tags);
  991. }
  992. setKeys(this.tags.values().toArray(new GitTag[this.tags.values().size()]));
  993. }
  994. @Override
  995. public void propertyChange (final PropertyChangeEvent evt) {
  996. if (RepositoryInfo.PROPERTY_TAGS.equals(evt.getPropertyName())) {
  997. RP.post(new Runnable() {
  998. @Override
  999. @SuppressWarnings("unchecked")
  1000. public void run () {
  1001. refreshTags((java.util.Map<String, GitTag>) evt.getNewValue());
  1002. }
  1003. });
  1004. }
  1005. }
  1006. }
  1007. private class TagNode extends RepositoryBrowserNode {
  1008. private boolean active;
  1009. private final String tagName;
  1010. private String revisionId;
  1011. private final String message;
  1012. private final PropertyChangeListener list;
  1013. public TagNode (File repository, GitTag tag) {
  1014. super(Children.LEAF, repository, Lookups.singleton(new Revision(tag.getTaggedObjectId(), tag.getTagName())));
  1015. tagName = tag.getTagName();
  1016. message = tag.getMessage();
  1017. revisionId = tag.getTaggedObjectId();
  1018. setIconBaseWithExtension("org/netbeans/modules/git/resources/icons/tag.png"); //NOI18N
  1019. RepositoryInfo info = RepositoryInfo.getInstance(repository);
  1020. if (info == null) {
  1021. LOG.log(Level.INFO, "TagNode() : Null info for {0}", repository); //NOI18N
  1022. list = null;
  1023. } else {
  1024. info.addPropertyChangeListener(WeakListeners.propertyChange(list = new PropertyChangeListener() {
  1025. @Override
  1026. public void propertyChange (PropertyChangeEvent evt) {
  1027. if (RepositoryInfo.PROPERTY_ACTIVE_BRANCH.equals(evt.getPropertyName()) || RepositoryInfo.PROPERTY_HEAD.equals(evt.getPropertyName())) {
  1028. refreshActiveBranch((GitBranch) evt.getNewValue());
  1029. }
  1030. }
  1031. }, info));
  1032. refreshActiveBranch(info.getActiveBranch());
  1033. }
  1034. }
  1035. @Override
  1036. public String getDisplayName () {
  1037. return getName(false);
  1038. }
  1039. @Override
  1040. public String getHtmlDisplayName() {
  1041. return getName(true);
  1042. }
  1043. @Override
  1044. public String getName() {
  1045. return getName(false);
  1046. }
  1047. public String getName (boolean html) {
  1048. StringBuilder sb = new StringBuilder();
  1049. if (active && html) {
  1050. sb.append("<html><strong>").append(tagName).append("</strong>"); //NOI18N
  1051. } else {
  1052. sb.append(tagName);
  1053. }
  1054. if (options.contains(Option.DISPLAY_COMMIT_IDS)) {
  1055. sb.append(" - ").append(revisionId); //NOI18N
  1056. }
  1057. return sb.toString();
  1058. }
  1059. private void refreshActiveBranch (GitBranch activeBranch) {
  1060. String oldHtmlName = getHtmlDisplayName();
  1061. if (activeBranch.getId().equals(revisionId)) {
  1062. active = true;
  1063. } else {
  1064. active = false;
  1065. }
  1066. String newHtmlName = getHtmlDisplayName();
  1067. if (!oldHtmlName.equals(newHtmlName)) {
  1068. fireDisplayNameChange(null, null);
  1069. }
  1070. }
  1071. @Override
  1072. public String getShortDescription () {
  1073. return message;
  1074. }
  1075. @Override