IDEA-186349 add utility methods to simplify implementing Navigatable
[idea/community.git] / platform / platform-api / src / com / intellij / util / OpenSourceUtil.java
1 // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.util;
3
4 import com.intellij.openapi.actionSystem.CommonDataKeys;
5 import com.intellij.openapi.actionSystem.DataContext;
6 import com.intellij.openapi.actionSystem.DataProvider;
7 import com.intellij.pom.Navigatable;
8 import com.intellij.pom.StatePreservingNavigatable;
9 import org.jetbrains.annotations.Nullable;
10
11 import static java.util.Arrays.asList;
12
13 public class OpenSourceUtil {
14
15   private OpenSourceUtil() {
16   }
17
18   public static void openSourcesFrom(DataContext context, boolean requestFocus) {
19     navigate(requestFocus, CommonDataKeys.NAVIGATABLE_ARRAY.getData(context));
20   }
21
22   public static void openSourcesFrom(DataProvider context, boolean requestFocus) {
23     navigate(requestFocus, CommonDataKeys.NAVIGATABLE_ARRAY.getData(context));
24   }
25
26   /**
27    * @return {@code true} if the specified {@code object} is {@link Navigatable} and supports navigation
28    */
29   public static boolean canNavigate(@Nullable Object object) {
30     return object instanceof Navigatable && ((Navigatable)object).canNavigate();
31   }
32
33   /**
34    * @return {@code true} if the specified {@code object} is {@link Navigatable} and supports navigation to source
35    */
36   public static boolean canNavigateToSource(@Nullable Object object) {
37     return object instanceof Navigatable && ((Navigatable)object).canNavigateToSource();
38   }
39
40   /**
41    * Invokes {@link #navigate(boolean, Navigatable...)} that always requests focus.
42    */
43   public static void navigate(@Nullable Navigatable... navigatables) {
44     navigate(true, navigatables);
45   }
46
47   /**
48    * Invokes {@link #navigate(boolean, boolean, Navigatable...)} that does not try to preserve a state of a corresponding editor.
49    */
50   public static void navigate(boolean requestFocus, @Nullable Navigatable... navigatables) {
51     navigate(requestFocus, false, navigatables);
52   }
53
54   /**
55    * Invokes {@link #navigate(boolean, boolean, Iterable)} if at least one navigatable exists
56    */
57   public static void navigate(boolean requestFocus, boolean tryNotToScroll, @Nullable Navigatable... navigatables) {
58     if (navigatables != null && navigatables.length > 0) navigate(requestFocus, tryNotToScroll, asList(navigatables));
59   }
60
61   /**
62    * Navigates to all available sources or to the first navigatable that represents non-source navigation.
63    *
64    * @param requestFocus   specifies whether a focus should be requested or not
65    * @param tryNotToScroll specifies whether a corresponding editor should preserve its state if it is possible
66    * @param navigatables   an iterable collection of navigatables
67    * @return {@code true} if at least one navigatable was processed, {@code false} otherwise
68    */
69   public static boolean navigate(boolean requestFocus, boolean tryNotToScroll, @Nullable Iterable<Navigatable> navigatables) {
70     if (navigatables == null) return false;
71     Navigatable nonSourceNavigatable = null;
72     boolean alreadyNavigatedToSource = false;
73     for (Navigatable navigatable : navigatables) {
74       if (navigateToSource(requestFocus, tryNotToScroll, navigatable)) {
75         alreadyNavigatedToSource = true;
76       }
77       else if (!alreadyNavigatedToSource && nonSourceNavigatable == null && canNavigate(navigatable)) {
78         nonSourceNavigatable = navigatable;
79       }
80     }
81     if (alreadyNavigatedToSource) return true;
82     if (nonSourceNavigatable == null) return false;
83     nonSourceNavigatable.navigate(requestFocus);
84     return true;
85   }
86
87   /**
88    * Navigates to all available sources of the specified navigatables.
89    *
90    * @param requestFocus   specifies whether a focus should be requested or not
91    * @param tryNotToScroll specifies whether a corresponding editor should preserve its state if it is possible
92    * @param navigatables   an iterable collection of navigatables
93    * @return {@code true} if at least one navigatable was processed, {@code false} otherwise
94    */
95   public static boolean navigateToSource(boolean requestFocus, boolean tryNotToScroll, @Nullable Iterable<Navigatable> navigatables) {
96     if (navigatables == null) return false;
97     boolean alreadyNavigatedToSource = false;
98     for (Navigatable navigatable : navigatables) {
99       if (navigateToSource(requestFocus, tryNotToScroll, navigatable)) {
100         alreadyNavigatedToSource = true;
101       }
102     }
103     return alreadyNavigatedToSource;
104   }
105
106   /**
107    * Navigates to source of the specified navigatable.
108    *
109    * @param requestFocus   specifies whether a focus should be requested or not
110    * @param tryNotToScroll specifies whether a corresponding editor should preserve its state if it is possible
111    * @return {@code true} if navigation is done, {@code false} otherwise
112    */
113   public static boolean navigateToSource(boolean requestFocus, boolean tryNotToScroll, @Nullable Navigatable navigatable) {
114     if (!canNavigateToSource(navigatable)) return false;
115     if (tryNotToScroll && navigatable instanceof StatePreservingNavigatable) {
116       ((StatePreservingNavigatable)navigatable).navigate(requestFocus, true);
117     }
118     else {
119       navigatable.navigate(requestFocus);
120     }
121     return true;
122   }
123 }