/platform/lang-impl/src/com/intellij/ide/todo/TodoView.java

https://bitbucket.org/nbargnesi/idea · Java · 354 lines · 288 code · 42 blank · 24 comment · 32 complexity · 9146ab015ecca33c6697ebfdce6c7691 MD5 · raw file

  1. /*
  2. * Copyright 2000-2012 JetBrains s.r.o.
  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.intellij.ide.todo;
  17. import com.intellij.ide.IdeBundle;
  18. import com.intellij.openapi.Disposable;
  19. import com.intellij.openapi.application.ApplicationManager;
  20. import com.intellij.openapi.application.ModalityState;
  21. import com.intellij.openapi.components.PersistentStateComponent;
  22. import com.intellij.openapi.components.State;
  23. import com.intellij.openapi.components.Storage;
  24. import com.intellij.openapi.components.StoragePathMacros;
  25. import com.intellij.openapi.fileTypes.FileTypeEvent;
  26. import com.intellij.openapi.fileTypes.FileTypeListener;
  27. import com.intellij.openapi.fileTypes.FileTypeManager;
  28. import com.intellij.openapi.progress.ProcessCanceledException;
  29. import com.intellij.openapi.progress.ProgressManager;
  30. import com.intellij.openapi.project.DumbService;
  31. import com.intellij.openapi.project.Project;
  32. import com.intellij.openapi.util.Disposer;
  33. import com.intellij.openapi.vcs.AbstractVcs;
  34. import com.intellij.openapi.vcs.ProjectLevelVcsManager;
  35. import com.intellij.openapi.vcs.VcsListener;
  36. import com.intellij.openapi.vcs.changes.ChangeListManager;
  37. import com.intellij.openapi.wm.ToolWindow;
  38. import com.intellij.ui.content.Content;
  39. import com.intellij.ui.content.ContentFactory;
  40. import com.intellij.ui.content.ContentManager;
  41. import com.intellij.util.messages.MessageBusConnection;
  42. import org.jdom.Element;
  43. import org.jetbrains.annotations.NonNls;
  44. import javax.swing.*;
  45. import javax.swing.tree.DefaultTreeModel;
  46. import java.beans.PropertyChangeEvent;
  47. import java.beans.PropertyChangeListener;
  48. import java.util.ArrayList;
  49. import java.util.List;
  50. /**
  51. * @author Vladimir Kondratyev
  52. */
  53. @State(
  54. name="TodoView",
  55. storages= {
  56. @Storage(
  57. file = StoragePathMacros.WORKSPACE_FILE
  58. )}
  59. )
  60. public class TodoView implements PersistentStateComponent<Element>, Disposable {
  61. private final Project myProject;
  62. private final ProjectLevelVcsManager myVCSManager;
  63. private ContentManager myContentManager;
  64. private CurrentFileTodosPanel myCurrentFileTodos;
  65. private TodoPanel myAllTodos;
  66. private ChangeListTodosPanel myChangeListTodos;
  67. private ScopeBasedTodosPanel myScopeBasedTodos;
  68. private final List<TodoPanel> myPanels;
  69. private final List<Content> myNotAddedContent;
  70. private int mySelectedIndex;
  71. private final TodoPanelSettings myCurrentPanelSettings;
  72. private final TodoPanelSettings myAllPanelSettings;
  73. private final TodoPanelSettings myChangeListTodosPanelSettings;
  74. @NonNls private static final String ATTRIBUTE_SELECTED_INDEX = "selected-index";
  75. @NonNls private static final String ELEMENT_TODO_PANEL = "todo-panel";
  76. @NonNls private static final String ATTRIBUTE_ID = "id";
  77. @NonNls private static final String VALUE_SELECTED_FILE = "selected-file";
  78. @NonNls private static final String VALUE_ALL = "all";
  79. @NonNls private static final String VALUE_DEFAULT_CHANGELIST = "default-changelist";
  80. private Content myChangeListTodosContent;
  81. private final MyVcsListener myVcsListener = new MyVcsListener();
  82. /* Invoked by reflection */
  83. TodoView(Project project, ProjectLevelVcsManager manager){
  84. myProject=project;
  85. myVCSManager = manager;
  86. myCurrentPanelSettings=new TodoPanelSettings();
  87. myAllPanelSettings=new TodoPanelSettings();
  88. myChangeListTodosPanelSettings = new TodoPanelSettings();
  89. myPanels = new ArrayList<TodoPanel>();
  90. myNotAddedContent = new ArrayList<Content>();
  91. myVCSManager.addVcsListener(myVcsListener);
  92. final MyPropertyChangeListener myPropertyChangeListener = new MyPropertyChangeListener();
  93. TodoConfiguration.getInstance().addPropertyChangeListener(myPropertyChangeListener,this);
  94. MessageBusConnection connection = myProject.getMessageBus().connect(this);
  95. connection.subscribe(FileTypeManager.TOPIC, new MyFileTypeListener());
  96. }
  97. public void loadState(Element element) {
  98. mySelectedIndex=0;
  99. try{
  100. mySelectedIndex=Integer.parseInt(element.getAttributeValue(ATTRIBUTE_SELECTED_INDEX));
  101. }catch(NumberFormatException ignored){
  102. //nothing to be done
  103. }
  104. //noinspection unchecked
  105. for (Element child : (Iterable<Element>)element.getChildren()) {
  106. if (ELEMENT_TODO_PANEL.equals(child.getName())) {
  107. String id = child.getAttributeValue(ATTRIBUTE_ID);
  108. if (VALUE_SELECTED_FILE.equals(id)) {
  109. myCurrentPanelSettings.readExternal(child);
  110. }
  111. else if (VALUE_ALL.equals(id)) {
  112. myAllPanelSettings.readExternal(child);
  113. }
  114. else if (VALUE_DEFAULT_CHANGELIST.equals(id)) {
  115. myChangeListTodosPanelSettings.readExternal(child);
  116. }
  117. else {
  118. throw new IllegalArgumentException("unknown id: " + id);
  119. }
  120. }
  121. }
  122. }
  123. public Element getState() {
  124. Element element = new Element("TodoView");
  125. if(myContentManager!=null){ // all panel were constructed
  126. Content content=myContentManager.getSelectedContent();
  127. element.setAttribute(ATTRIBUTE_SELECTED_INDEX,Integer.toString(myContentManager.getIndexOfContent(content)));
  128. }
  129. Element selectedFileElement=new Element(ELEMENT_TODO_PANEL);
  130. selectedFileElement.setAttribute(ATTRIBUTE_ID,VALUE_SELECTED_FILE);
  131. myCurrentPanelSettings.writeExternal(selectedFileElement);
  132. element.addContent(selectedFileElement);
  133. Element allElement=new Element(ELEMENT_TODO_PANEL);
  134. allElement.setAttribute(ATTRIBUTE_ID, VALUE_ALL);
  135. myAllPanelSettings.writeExternal(allElement);
  136. element.addContent(allElement);
  137. Element changeListElement=new Element(ELEMENT_TODO_PANEL);
  138. changeListElement.setAttribute(ATTRIBUTE_ID, VALUE_DEFAULT_CHANGELIST);
  139. myChangeListTodosPanelSettings.writeExternal(changeListElement);
  140. element.addContent(changeListElement);
  141. return element;
  142. }
  143. public void dispose() {
  144. myVCSManager.removeVcsListener(myVcsListener);
  145. }
  146. public void initToolWindow(ToolWindow toolWindow) {
  147. // Create panels
  148. Content allTodosContent= ContentFactory.SERVICE.getInstance().createContent(null, IdeBundle.message("title.project"),false);
  149. myAllTodos=new TodoPanel(myProject,myAllPanelSettings,false,allTodosContent){
  150. protected TodoTreeBuilder createTreeBuilder(JTree tree, DefaultTreeModel treeModel, Project project){
  151. AllTodosTreeBuilder builder=new AllTodosTreeBuilder(tree,treeModel,project);
  152. builder.init();
  153. return builder;
  154. }
  155. };
  156. allTodosContent.setComponent(myAllTodos);
  157. Disposer.register(this, myAllTodos);
  158. Content currentFileTodosContent=
  159. ContentFactory.SERVICE.getInstance().createContent(null,IdeBundle.message("title.todo.current.file"),false);
  160. myCurrentFileTodos=new CurrentFileTodosPanel(myProject,myCurrentPanelSettings,currentFileTodosContent){
  161. protected TodoTreeBuilder createTreeBuilder(JTree tree,DefaultTreeModel treeModel,Project project){
  162. CurrentFileTodosTreeBuilder builder=new CurrentFileTodosTreeBuilder(tree,treeModel,project);
  163. builder.init();
  164. return builder;
  165. }
  166. };
  167. Disposer.register(this, myCurrentFileTodos);
  168. currentFileTodosContent.setComponent(myCurrentFileTodos);
  169. myChangeListTodosContent = ContentFactory.SERVICE.getInstance()
  170. .createContent(null, IdeBundle.message("changelist.todo.title",
  171. ChangeListManager.getInstance(myProject).getDefaultChangeList().getName()),
  172. false);
  173. myChangeListTodos = new ChangeListTodosPanel(myProject, myCurrentPanelSettings, myChangeListTodosContent) {
  174. protected TodoTreeBuilder createTreeBuilder(JTree tree, DefaultTreeModel treeModel, Project project) {
  175. ChangeListTodosTreeBuilder builder = new ChangeListTodosTreeBuilder(tree, treeModel, project);
  176. builder.init();
  177. return builder;
  178. }
  179. };
  180. Disposer.register(this, myChangeListTodos);
  181. myChangeListTodosContent.setComponent(myChangeListTodos);
  182. Content scopeBasedTodoContent = ContentFactory.SERVICE.getInstance().createContent(null, "Scope Based", false);
  183. myScopeBasedTodos = new ScopeBasedTodosPanel(myProject, myCurrentPanelSettings, scopeBasedTodoContent);
  184. Disposer.register(this, myScopeBasedTodos);
  185. scopeBasedTodoContent.setComponent(myScopeBasedTodos);
  186. myContentManager=toolWindow.getContentManager();
  187. myContentManager.addContent(allTodosContent);
  188. myContentManager.addContent(currentFileTodosContent);
  189. myContentManager.addContent(scopeBasedTodoContent);
  190. if (myVCSManager.getAllActiveVcss().length > 0) {
  191. myVcsListener.myIsVisible = true;
  192. myContentManager.addContent(myChangeListTodosContent);
  193. }
  194. for (Content content : myNotAddedContent) {
  195. myContentManager.addContent(content);
  196. }
  197. myChangeListTodosContent.setCloseable(false);
  198. allTodosContent.setCloseable(false);
  199. currentFileTodosContent.setCloseable(false);
  200. scopeBasedTodoContent.setCloseable(false);
  201. Content content=myContentManager.getContent(mySelectedIndex);
  202. content = content == null ? allTodosContent : content;
  203. myContentManager.setSelectedContent(content);
  204. myPanels.add(myAllTodos);
  205. myPanels.add(myChangeListTodos);
  206. myPanels.add(myCurrentFileTodos);
  207. myPanels.add(myScopeBasedTodos);
  208. }
  209. private final class MyVcsListener implements VcsListener {
  210. private boolean myIsVisible;
  211. public void directoryMappingChanged() { // todo ?
  212. ApplicationManager.getApplication().invokeLater(new Runnable(){
  213. public void run() {
  214. if (myContentManager == null) return; //was not initialized yet
  215. if (myProject.isDisposed()) return; //already disposed
  216. final AbstractVcs[] vcss = myVCSManager.getAllActiveVcss();
  217. if (myIsVisible && vcss.length == 0) {
  218. myContentManager.removeContent(myChangeListTodosContent, false);
  219. myIsVisible = false;
  220. }
  221. else if (!myIsVisible && vcss.length > 0) {
  222. myContentManager.addContent(myChangeListTodosContent);
  223. myIsVisible = true;
  224. }
  225. }
  226. }, ModalityState.NON_MODAL);
  227. }
  228. }
  229. private final class MyPropertyChangeListener implements PropertyChangeListener{
  230. public void propertyChange(PropertyChangeEvent e){
  231. if (TodoConfiguration.PROP_TODO_PATTERNS.equals(e.getPropertyName()) || TodoConfiguration.PROP_TODO_FILTERS.equals(e.getPropertyName())) {
  232. _updateFilters();
  233. }
  234. }
  235. private void _updateFilters() {
  236. try {
  237. updateFilters();
  238. }
  239. catch (ProcessCanceledException e) {
  240. DumbService.getInstance(myProject).smartInvokeLater(new Runnable() {
  241. public void run() {
  242. _updateFilters();
  243. }
  244. }, ModalityState.NON_MODAL);
  245. }
  246. }
  247. private void updateFilters(){
  248. for (TodoPanel panel : myPanels) {
  249. panel.updateTodoFilter();
  250. }
  251. }
  252. }
  253. private final class MyFileTypeListener extends FileTypeListener.Adapter {
  254. @Override
  255. public void fileTypesChanged(FileTypeEvent e){
  256. // this invokeLater guaranties that this code will be invoked after
  257. // PSI gets the same event.
  258. DumbService.getInstance(myProject).smartInvokeLater(new Runnable() {
  259. public void run() {
  260. if (myProject.isDisposed()) return;
  261. ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable(){
  262. public void run(){
  263. if (myAllTodos == null) return;
  264. ApplicationManager.getApplication().runReadAction(
  265. new Runnable(){
  266. public void run(){
  267. for (TodoPanel panel : myPanels) {
  268. panel.rebuildCache();
  269. }
  270. }
  271. }
  272. );
  273. ApplicationManager.getApplication().invokeLater(new Runnable(){
  274. public void run(){
  275. for (TodoPanel panel : myPanels) {
  276. panel.updateTree();
  277. }
  278. }
  279. }, ModalityState.NON_MODAL);
  280. }
  281. }, IdeBundle.message("progress.looking.for.todos"), false, myProject);
  282. }
  283. });
  284. }
  285. }
  286. public void addCustomTodoView(final TodoTreeBuilderFactory factory, final String title, final TodoPanelSettings settings) {
  287. final Content content = ContentFactory.SERVICE.getInstance().createContent(null, title, true);
  288. final ChangeListTodosPanel panel = new ChangeListTodosPanel(myProject, settings, content) {
  289. @Override
  290. protected TodoTreeBuilder createTreeBuilder(JTree tree, DefaultTreeModel treeModel, Project project) {
  291. final TodoTreeBuilder todoTreeBuilder = factory.createTreeBuilder(tree, treeModel, project);
  292. todoTreeBuilder.init();
  293. return todoTreeBuilder;
  294. }
  295. };
  296. content.setComponent(panel);
  297. Disposer.register(this, panel);
  298. if (myContentManager != null) {
  299. myContentManager.addContent(content);
  300. } else {
  301. myNotAddedContent.add(content);
  302. }
  303. myPanels.add(panel);
  304. content.setCloseable(true);
  305. content.setDisposer(new Disposable() {
  306. @Override
  307. public void dispose() {
  308. myPanels.remove(panel);
  309. }
  310. });
  311. }
  312. }