1
+ package com.coder.gateway.views.steps
2
+
3
+ import com.coder.gateway.CoderGatewayBundle
4
+ import com.coder.gateway.models.CoderWorkspacesWizardModel
5
+ import com.coder.gateway.sdk.CoderRestClientService
6
+ import com.coder.gateway.views.LazyBrowserLink
7
+ import com.intellij.ide.IdeBundle
8
+ import com.intellij.openapi.Disposable
9
+ import com.intellij.openapi.application.ApplicationManager
10
+ import com.intellij.openapi.ui.ComboBox
11
+ import com.intellij.openapi.wm.impl.welcomeScreen.WelcomeScreenUIManager
12
+ import com.intellij.remote.AuthType
13
+ import com.intellij.remote.RemoteCredentialsHolder
14
+ import com.intellij.ui.AnimatedIcon
15
+ import com.intellij.ui.IconManager
16
+ import com.intellij.ui.components.JBTextField
17
+ import com.intellij.ui.dsl.builder.BottomGap
18
+ import com.intellij.ui.dsl.builder.RowLayout
19
+ import com.intellij.ui.dsl.builder.TopGap
20
+ import com.intellij.ui.dsl.builder.panel
21
+ import com.intellij.ui.dsl.gridLayout.HorizontalAlign
22
+ import com.intellij.util.ui.JBFont
23
+ import com.intellij.util.ui.JBUI
24
+ import com.intellij.util.ui.UIUtil
25
+ import com.jetbrains.gateway.api.GatewayUI
26
+ import com.jetbrains.gateway.ssh.CachingProductsJsonWrapper
27
+ import com.jetbrains.gateway.ssh.IdeStatus
28
+ import com.jetbrains.gateway.ssh.IdeWithStatus
29
+ import com.jetbrains.gateway.ssh.IntelliJPlatformProduct
30
+ import com.jetbrains.gateway.ssh.guessOs
31
+ import kotlinx.coroutines.CoroutineScope
32
+ import kotlinx.coroutines.Dispatchers
33
+ import kotlinx.coroutines.launch
34
+ import kotlinx.coroutines.withContext
35
+ import java.awt.Component
36
+ import java.awt.FlowLayout
37
+ import java.util.logging.Logger
38
+ import javax.swing.ComboBoxModel
39
+ import javax.swing.DefaultComboBoxModel
40
+ import javax.swing.JLabel
41
+ import javax.swing.JList
42
+ import javax.swing.JPanel
43
+ import javax.swing.ListCellRenderer
44
+ import javax.swing.SwingConstants
45
+
46
+ class CoderLocateRemoteProjectStepView : CoderWorkspacesWizardStep , Disposable {
47
+ private val cs = CoroutineScope (Dispatchers .Main )
48
+ private val coderClient: CoderRestClientService = ApplicationManager .getApplication().getService(CoderRestClientService ::class .java)
49
+
50
+ private val spinner = JLabel (" " , AnimatedIcon .Default (), SwingConstants .LEFT )
51
+ private var ideComboBoxModel = DefaultComboBoxModel <IdeWithStatus >()
52
+
53
+ private lateinit var titleLabel: JLabel
54
+ private lateinit var wizard: CoderWorkspacesWizardModel
55
+ private lateinit var cbIDE: IDEComboBox
56
+ private lateinit var tfProject: JBTextField
57
+ private lateinit var terminalLink: LazyBrowserLink
58
+
59
+ override val component = panel {
60
+ indent {
61
+ row {
62
+ titleLabel = label(" " ).applyToComponent {
63
+ font = JBFont .h3().asBold()
64
+ icon = IconManager .getInstance().getIcon(" coder_logo_16.svg" , this @CoderLocateRemoteProjectStepView::class .java)
65
+ }.component
66
+ }.bottomGap(BottomGap .MEDIUM )
67
+
68
+ row {
69
+ label(" IDE:" )
70
+ cbIDE = cell(IDEComboBox (ideComboBoxModel).apply {
71
+ renderer = IDECellRenderer ()
72
+ }).resizableColumn().horizontalAlign(HorizontalAlign .FILL ).comment(" The IDE will be downloaded from jetbrains.com" ).component
73
+ cell()
74
+ }.topGap(TopGap .NONE ).layout(RowLayout .PARENT_GRID )
75
+
76
+ row {
77
+ label(" Project directory:" )
78
+ tfProject = textField()
79
+ .resizableColumn()
80
+ .horizontalAlign(HorizontalAlign .FILL )
81
+ .applyToComponent {
82
+ this .text = " /home/coder/workspace/"
83
+ }.component
84
+ cell()
85
+ }.topGap(TopGap .NONE ).bottomGap(BottomGap .NONE ).layout(RowLayout .PARENT_GRID )
86
+ row {
87
+ cell()
88
+ terminalLink = cell(
89
+ LazyBrowserLink (
90
+ IconManager .getInstance().getIcon(" open_terminal.svg" , this ::class .java),
91
+ " Open Terminal"
92
+ )
93
+ ).component
94
+ }.topGap(TopGap .NONE ).layout(RowLayout .PARENT_GRID )
95
+ }
96
+ }.apply { background = WelcomeScreenUIManager .getMainAssociatedComponentBackground() }
97
+
98
+ override val previousActionText = IdeBundle .message(" button.back" )
99
+ override val nextActionText = CoderGatewayBundle .message(" gateway.connector.view.coder.remoteproject.next.text" )
100
+
101
+ override fun onInit (wizardModel : CoderWorkspacesWizardModel ) {
102
+ wizard = wizardModel
103
+ val selectedWorkspace = wizardModel.selectedWorkspace
104
+ if (selectedWorkspace == null ) {
105
+ logger.warning(" No workspace was selected. Please go back to the previous step and select a Coder Workspace" )
106
+ return
107
+ }
108
+
109
+ titleLabel.text = CoderGatewayBundle .message(" gateway.connector.view.coder.remoteproject.choose.text" , selectedWorkspace.name)
110
+ terminalLink.url = " ${coderClient.coderURL} /@${coderClient.me.username} /${selectedWorkspace.name} .coder/terminal"
111
+
112
+ cs.launch {
113
+ logger.info(" Retrieving available IDE's for ${selectedWorkspace.name} workspace..." )
114
+ val workspaceOS = withContext(Dispatchers .IO ) {
115
+ RemoteCredentialsHolder ().apply {
116
+ setHost(" coder.${selectedWorkspace.name} " )
117
+ userName = " coder"
118
+ authType = AuthType .OPEN_SSH
119
+ }.guessOs
120
+ }
121
+ logger.info(" Resolved OS and Arch for ${selectedWorkspace.name} is: $workspaceOS " )
122
+ val idesWithStatus = IntelliJPlatformProduct .values()
123
+ .filter { it.showInGateway }
124
+ .flatMap { CachingProductsJsonWrapper .getAvailableIdes(it, workspaceOS) }
125
+ .map { ide -> IdeWithStatus (ide.product, ide.buildNumber, IdeStatus .DOWNLOAD , ide.downloadLink, ide.presentableVersion) }
126
+
127
+ if (idesWithStatus.isNullOrEmpty()) {
128
+ logger.warning(" Could not resolve any IDE for workspace ${selectedWorkspace.name} , probably $workspaceOS is not supported by Gateway" )
129
+ } else {
130
+ cbIDE.remove(spinner)
131
+ ideComboBoxModel.addAll(idesWithStatus)
132
+ cbIDE.selectedIndex = 0
133
+ }
134
+ }
135
+ }
136
+
137
+ override fun onNext (wizardModel : CoderWorkspacesWizardModel ): Boolean {
138
+ val selectedIDE = cbIDE.selectedItem ? : return false
139
+
140
+ cs.launch {
141
+
142
+ GatewayUI .getInstance().connect(
143
+ mapOf (
144
+ " type" to " coder" ,
145
+ " coder_workspace_hostname" to " coder.${wizardModel.selectedWorkspace?.name} " ,
146
+ " project_path" to tfProject.text,
147
+ " ide_product_code" to " ${selectedIDE.product.productCode} " ,
148
+ " ide_build_number" to " ${selectedIDE.buildNumber} " ,
149
+ " ide_download_link" to " ${selectedIDE.source} "
150
+ )
151
+ )
152
+ }
153
+ return true
154
+ }
155
+
156
+ override fun dispose () {
157
+ }
158
+
159
+ companion object {
160
+ val logger = Logger .getLogger(CoderLocateRemoteProjectStepView ::class .java.simpleName)
161
+ }
162
+
163
+ private class IDEComboBox (model : ComboBoxModel <IdeWithStatus >) : ComboBox<IdeWithStatus>(model) {
164
+ override fun getSelectedItem (): IdeWithStatus ? {
165
+ return super .getSelectedItem() as IdeWithStatus ?
166
+ }
167
+ }
168
+
169
+ private class IDECellRenderer : ListCellRenderer <IdeWithStatus > {
170
+ override fun getListCellRendererComponent (list : JList <out IdeWithStatus >? , ideWithStatus : IdeWithStatus ? , index : Int , isSelected : Boolean , cellHasFocus : Boolean ): Component {
171
+ return if (ideWithStatus == null && index == - 1 ) {
172
+ JPanel ().apply {
173
+ layout = FlowLayout (FlowLayout .LEFT )
174
+ add(JLabel (" Retrieving products..." , AnimatedIcon .Default (), SwingConstants .LEFT ))
175
+ }
176
+ } else if (ideWithStatus != null ) {
177
+ JPanel ().apply {
178
+ layout = FlowLayout (FlowLayout .LEFT )
179
+ add(JLabel (ideWithStatus.product.ideName, ideWithStatus.product.icon, SwingConstants .LEFT ))
180
+ add(JLabel (" ${ideWithStatus.product.productCode} ${ideWithStatus.presentableVersion} ${ideWithStatus.buildNumber} | ${ideWithStatus.status.name.toLowerCase()} " ).apply {
181
+ foreground = UIUtil .getLabelDisabledForeground()
182
+ })
183
+ background = JBUI .CurrentTheme .List .background(isSelected, cellHasFocus)
184
+ }
185
+ } else {
186
+ JPanel ()
187
+ }
188
+ }
189
+ }
190
+ }
0 commit comments