RIDER-77734 Compound run configs are duplicated if it's started out of the toolbar
[idea/community.git] / platform / execution-impl / src / com / intellij / execution / runToolbar / RunToolbarMainWidgetComponent.kt
1 // Copyright 2000-2021 JetBrains s.r.o. and contributors. 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.execution.runToolbar
3
4 import com.intellij.execution.runToolbar.data.RWSlotManagerState
5 import com.intellij.execution.runToolbar.data.RWStateListener
6 import com.intellij.ide.DataManager
7 import com.intellij.openapi.actionSystem.*
8 import com.intellij.openapi.diagnostic.Logger
9 import com.intellij.openapi.project.Project
10 import com.intellij.openapi.util.Disposer
11 import com.intellij.openapi.wm.IdeFrame
12 import java.awt.event.ContainerEvent
13 import java.awt.event.ContainerListener
14 import javax.swing.SwingUtilities
15
16 class RunToolbarMainWidgetComponent(val presentation: Presentation, place: String, group: ActionGroup) :
17   FixWidthSegmentedActionToolbarComponent(place, group) {
18   companion object {
19     private val LOG = Logger.getInstance(RunToolbarMainWidgetComponent::class.java)
20     private var counter: MutableMap<Project, Int> = mutableMapOf()
21   }
22
23   override fun logNeeded(): Boolean = RunToolbarProcess.logNeeded
24
25   private var project: Project? = null
26     set(value) {
27       if(field == value) return
28       field?.let {
29         remove(it)
30       }
31
32       field = value
33
34       field?.let {
35         add(it)
36       }
37     }
38
39   private var popupController: RunToolbarPopupController? = null
40
41   private val componentListener = object : ContainerListener {
42     override fun componentAdded(e: ContainerEvent) {
43       rebuildPopupControllerComponent()
44     }
45
46     override fun componentRemoved(e: ContainerEvent) {
47       rebuildPopupControllerComponent()
48     }
49   }
50
51   private val managerStateListener = object : RWStateListener {
52     override fun stateChanged(state: RWSlotManagerState) {
53       updateState()
54     }
55   }
56
57   private var state: RunToolbarMainSlotState? = null
58
59   private fun updateState() {
60     state = project?.let {
61       val slotManager = RunToolbarSlotManager.getInstance(it)
62       val value = when (slotManager.getState()) {
63         RWSlotManagerState.SINGLE_MAIN -> {
64           RunToolbarMainSlotState.PROCESS
65         }
66         RWSlotManagerState.SINGLE_PLAIN,
67         RWSlotManagerState.MULTIPLE -> {
68           if(isOpened) RunToolbarMainSlotState.CONFIGURATION else RunToolbarMainSlotState.INFO
69         }
70         RWSlotManagerState.INACTIVE -> {
71           RunToolbarMainSlotState.CONFIGURATION
72         }
73         RWSlotManagerState.MULTIPLE_WITH_MAIN -> {
74           if(isOpened) RunToolbarMainSlotState.PROCESS else RunToolbarMainSlotState.INFO
75         }
76       }
77
78       value
79     }
80
81     if(RunToolbarProcess.logNeeded) LOG.info("MAIN SLOT state updated: $state RunToolbar")
82   }
83
84   override fun traceState(lastIds: List<String>, filteredIds: List<String>, ides: List<String>) {
85     if(logNeeded() && filteredIds != lastIds) LOG.info("MAIN SLOT state: ${state} new filtered: ${filteredIds}} visible: $ides RunToolbar")
86   }
87
88   internal var isOpened = false
89     set(value) {
90       if(field == value) return
91
92       field = value
93       if(RunToolbarProcess.logNeeded) LOG.info("MAIN SLOT isOpened: $isOpened RunToolbar")
94       updateState()
95
96       if (RunToolbarProcess.isExperimentalUpdatingEnabled) {
97         forceUpdate()
98       }
99     }
100
101   override fun isSuitableAction(action: AnAction): Boolean {
102     return state?.let {
103       if(action is RTBarAction) {
104         action.checkMainSlotVisibility(it)
105       } else true
106     } ?: true
107   }
108
109   override fun addNotify() {
110     super.addNotify()
111
112     (SwingUtilities.getWindowAncestor(this) as? IdeFrame)?.project?.let {
113       project = it
114
115       RUN_CONFIG_WIDTH = RunToolbarSettings.getInstance(it).getRunConfigWidth()
116
117     }
118   }
119
120   override fun updateWidthHandler() {
121     super.updateWidthHandler()
122     project?.let {
123       RunToolbarSettings.getInstance(it).setRunConfigWidth(RUN_CONFIG_WIDTH)
124     }
125   }
126
127   private fun rebuildPopupControllerComponent() {
128     popupController?.let {
129       it.updateControllerComponents(components.filter{it is PopupControllerComponent}.toMutableList())
130     }
131   }
132
133   override fun removeNotify() {
134     project = null
135     super.removeNotify()
136   }
137
138   private fun add(project: Project) {
139     popupController = RunToolbarPopupController(project, this)
140
141     val value = counter.getOrDefault(project, 0) + 1
142     counter[project] = value
143     val slotManager = RunToolbarSlotManager.getInstance(project)
144     DataManager.registerDataProvider(component, DataProvider { key ->
145       when {
146         RunToolbarProcessData.RW_SLOT.`is`(key) -> {
147           slotManager.mainSlotData.id
148         }
149         RunToolbarData.RUN_TOOLBAR_DATA_KEY.`is`(key) -> {
150           slotManager.mainSlotData
151         }
152         RunToolbarData.RUN_TOOLBAR_POPUP_STATE_KEY.`is`(key) -> {
153           isOpened
154         }
155         RunToolbarData.RUN_TOOLBAR_MAIN_STATE.`is`(key) -> {
156           state
157         }
158         else -> null
159       }
160     })
161
162     if (value == 1) {
163       slotManager.stateListeners.addListener(managerStateListener)
164       slotManager.active = true
165     }
166
167     rebuildPopupControllerComponent()
168     addContainerListener(componentListener)
169   }
170
171   private fun remove(project: Project) {
172     RunToolbarSlotManager.getInstance(project).stateListeners.removeListener(managerStateListener)
173     counter[project]?.let {
174       val value = maxOf(it - 1, 0)
175       counter[project] = value
176       if (value == 0) {
177         RunToolbarSlotManager.getInstance(project).active = false
178         counter.remove(project)
179       }
180     }
181
182     removeContainerListener(componentListener)
183     popupController?.let {
184       if(!Disposer.isDisposed(it)) {
185         Disposer.dispose(it)
186       }
187     }
188     popupController = null
189     state = null
190   }
191 }
192