diff --git a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/api/call/ApiCaller.kt b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/api/call/ApiCaller.kt
index 7bc93e0fd..910af4f15 100644
--- a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/api/call/ApiCaller.kt
+++ b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/api/call/ApiCaller.kt
@@ -43,14 +43,20 @@ class ApiCaller {
return
}
- val apiCallUI = actionContext.instance(ApiCallUI::class)
- apiCallUI.updateRequestList(requests)
- apiCallUI.showUI()
- val uiWeakReference = WeakReference(apiCallUI)
- project.putUserData(API_CALL_UI, uiWeakReference)
- actionContext.on(EventKey.ON_COMPLETED) {
- project.putUserData(API_CALL_UI, null)
- uiWeakReference.clear()
+ actionContext.runInSwingUI {
+
+ val apiCallUI = actionContext.instance(ApiCallUI::class)
+ apiCallUI.updateRequestList(requests)
+ apiCallUI.showUI()
+
+ actionContext.runAsync {
+ val uiWeakReference = WeakReference(apiCallUI)
+ project.putUserData(API_CALL_UI, uiWeakReference)
+ actionContext.on(EventKey.ON_COMPLETED) {
+ project.putUserData(API_CALL_UI, null)
+ uiWeakReference.clear()
+ }
+ }
}
} catch (e: Exception) {
logger.traceError("Apis exported failed", e)
diff --git a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/api/export/suv/SuvApiExporter.kt b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/api/export/suv/SuvApiExporter.kt
index fdef71112..997bd4b8b 100644
--- a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/api/export/suv/SuvApiExporter.kt
+++ b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/api/export/suv/SuvApiExporter.kt
@@ -83,11 +83,11 @@ open class SuvApiExporter {
return
}
- val multipleApiExportDialog = actionContext.instance { SuvApiExportDialog() }
+ actionContext.runInSwingUI {
- UIUtils.show(multipleApiExportDialog)
+ val multipleApiExportDialog = actionContext.instance { SuvApiExportDialog() }
- actionContext.runInSwingUI {
+ UIUtils.show(multipleApiExportDialog)
multipleApiExportDialog.setOnChannelChanged { channel ->
if (channel == null) {
diff --git a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/dialog/ApiCallDialog.form b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/dialog/ApiCallDialog.form
deleted file mode 100644
index f1b80b2e0..000000000
--- a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/dialog/ApiCallDialog.form
+++ /dev/null
@@ -1,456 +0,0 @@
-
-
\ No newline at end of file
diff --git a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/dialog/ApiCallDialog.kt b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/dialog/ApiCallDialog.kt
index 54770c721..6f69f5b3a 100644
--- a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/dialog/ApiCallDialog.kt
+++ b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/dialog/ApiCallDialog.kt
@@ -6,9 +6,14 @@ import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
import com.intellij.openapi.ui.Messages
import com.intellij.ui.BooleanTableCellEditor
import com.intellij.ui.BooleanTableCellRenderer
+import com.intellij.ui.CollectionListModel
+import com.intellij.ui.components.JBLabel
+import com.intellij.ui.components.JBList
import com.intellij.ui.components.JBTabbedPane
import com.intellij.ui.table.JBTable
import com.intellij.util.ui.ComboBoxCellEditor
+import com.intellij.util.ui.JBUI
+import com.intellij.util.ui.components.BorderLayoutPanel
import com.itangcent.cache.HttpContextCacheHelper
import com.itangcent.common.constant.HttpMethod
import com.itangcent.common.logger.Log
@@ -28,7 +33,6 @@ import com.itangcent.idea.swing.onSelect
import com.itangcent.idea.swing.onTextChange
import com.itangcent.idea.utils.*
import com.itangcent.intellij.extend.rx.ThrottleHelper
-import com.itangcent.intellij.extend.rx.throttle
import com.itangcent.intellij.extend.withBoundary
import com.itangcent.intellij.file.LocalFileRepository
import com.itangcent.intellij.psi.PsiClassUtils
@@ -38,9 +42,12 @@ import org.apache.commons.lang3.exception.ExceptionUtils
import org.apache.http.entity.ContentType
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
+import java.awt.BorderLayout
+import java.awt.Dimension
+import java.awt.GridBagConstraints
+import java.awt.GridBagLayout
import java.awt.event.*
import java.io.Closeable
-import java.util.concurrent.TimeUnit
import javax.swing.*
import javax.swing.event.TableModelListener
import javax.swing.table.DefaultTableModel
@@ -49,43 +56,316 @@ import javax.swing.table.TableModel
import javax.swing.text.JTextComponent
-class ApiCallDialog : ContextDialog(), ApiCallUI {
- private lateinit var contentPane: JPanel
+@Suppress("DialogTitleCapitalization")
+private class ApiCallPanel : BorderLayoutPanel() {
+
+ val searchTextField: JTextField
+ val apisListPanel: JScrollPane
+ val apisJList: JBList
+
+ val methodLabel: JLabel
+ val hostComboBox: JComboBox
+ val pathTextField: JTextField
+
+ val callButton: JButton
+
+ val requestBodyTextArea: JTextArea
+ val requestHeadersTextArea: JTextArea
+ val contentTypeComboBox: JComboBox
+ val formTable: JBTable
+
+ val requestPanel: JBTabbedPane
+
+ val paramPanel: JPanel
+ val paramsLabel: JLabel
+ val paramsTextField: JTextField
+
+ val contentTypePanel: JPanel
+ val contentTypeLabel: JLabel
+
+ val responsePanel: JBTabbedPane
+ val formatOrRawButton: JButton
+ val saveButton: JButton
+ val statusLabel: JLabel
+ val responseActionPanel: JPanel
+ val responseTextArea: JTextArea
+ val responseHeadersTextArea: JTextArea
+
+
+ init {
+ this.layout = GridBagLayout() // Set the layout manager to GridBagLayout
+ this.border = BorderFactory.createEmptyBorder(10, 10, 10, 10)
+
+ searchTextField = JTextField()
+
+ // API List Panel
+ apisJList = JBList()
+ apisListPanel = JScrollPane(apisJList)
+
+ // Left Panel for Search and API List
+ val leftPanel = JPanel(BorderLayout()).apply {
+ // Remove any default margins or padding
+ border = BorderFactory.createEmptyBorder(0, 0, 0, 0)
+ }
+ leftPanel.add(searchTextField, BorderLayout.NORTH)
+ leftPanel.add(apisListPanel, BorderLayout.CENTER)
+
+ // Add Left Panel to ApiCallPanel
+ val gbcLeftPanel = GridBagConstraints().apply {
+ fill = GridBagConstraints.BOTH
+ gridx = 0
+ gridy = 1
+ weightx = 0.5
+ weighty = 1.0
+ }
+ this.add(leftPanel, gbcLeftPanel)
+
+ // Right Panel
+
+ val rightPanel = JPanel(GridBagLayout()) // Set the layout manager to GridBagLayout
+ val gbcRightPanel = GridBagConstraints().apply {
+ fill = GridBagConstraints.BOTH
+ gridx = 1
+ gridy = 0
+ gridheight = 2
+ weightx = 0.5
+ weighty = 1.0
+ }
+ this.add(rightPanel, gbcRightPanel) // Use BorderLayout.EAST as the constraint string
+
+ // Top Panel within Right Panel
+ methodLabel = JBLabel("GET")
+ hostComboBox = JComboBox().apply {
+ isEditable = true // Make it editable
+ // Optional: Set a maximum width if desired
+ setMaximumSize(Dimension(250, getPreferredSize().height))
+ }
+ pathTextField = JTextField("path")
+ callButton = JButton("Call")
+
+ val topPanel = JPanel(GridBagLayout())
+ // methodLabel with fixed width
+ methodLabel.setPreferredSize(Dimension(50, methodLabel.getPreferredSize().height)) // Example width
+ topPanel.add(methodLabel, GridBagConstraints().apply {
+ gridx = 0
+ gridy = 0
+ weightx = 0.0 // No extra space
+ fill = GridBagConstraints.NONE
+ })
+
+ // hostComboBox with fixed width
+ topPanel.add(hostComboBox, GridBagConstraints().apply {
+ gridx = 1
+ weightx = 0.3 // Adjust this to control the width of the combo box
+ fill = GridBagConstraints.HORIZONTAL // Make it grow/shrink horizontally as needed
+ })
+
+ // pathTextField to fill the rest
+ topPanel.add(pathTextField, GridBagConstraints().apply {
+ gridx = 2
+ weightx = 1.0 // Take up remaining space
+ fill = GridBagConstraints.HORIZONTAL
+ })
+
+ // callButton with fixed width
+ callButton.preferredSize = Dimension(80, callButton.getPreferredSize().height) // Example width
+ topPanel.add(callButton, GridBagConstraints().apply {
+ gridx = 3
+ weightx = 0.0 // No extra space
+ fill = GridBagConstraints.NONE
+ })
+
+ // Add topPanel to rightPanel
+ rightPanel.add(topPanel, GridBagConstraints().apply {
+ fill = GridBagConstraints.HORIZONTAL
+ gridx = 0
+ gridy = 0
+ weightx = 1.0 // Make it fill the entire width of rightPanel
+ anchor = GridBagConstraints.LINE_START // Align to the left
+ insets = JBUI.insetsLeft(8) // Add a 15-pixel margin to the left
+ })
- private lateinit var apisListPanel: JPanel
- private lateinit var apisJList: JList
- private lateinit var apisPopMenu: JPopupMenu
+// Param Panel within Right Panel
+ paramsLabel = JBLabel("Params")
+ paramsTextField = JTextField()
+ paramPanel = JPanel(GridBagLayout()) // Use GridBagLayout
+ val gbcParam = GridBagConstraints()
+
+// paramsLabel with fixed width
+ gbcParam.gridx = 0
+ gbcParam.gridy = 0
+ gbcParam.weightx = 0.0 // No extra space
+ gbcParam.fill = GridBagConstraints.NONE
+ paramPanel.add(paramsLabel, gbcParam)
+
+// paramsTextField to fill the rest
+ gbcParam.gridx = 1
+ gbcParam.weightx = 1.0 // Take up remaining space
+ gbcParam.fill = GridBagConstraints.HORIZONTAL
+ paramPanel.add(paramsTextField, gbcParam)
+
+ rightPanel.add(paramPanel, GridBagConstraints().apply {
+ fill = GridBagConstraints.HORIZONTAL
+ gridy = 1
+ weightx = 1.0
+ anchor = GridBagConstraints.LINE_START // Align to the left
+ insets = JBUI.insetsLeft(8) // Add a 15-pixel margin to the left
+ })
- private lateinit var rightPanel: JPanel
+// ContentType Panel within Right Panel
+ contentTypeLabel = JLabel("ContentType")
+ contentTypeComboBox = JComboBox()
+ contentTypePanel = JPanel(GridBagLayout()) // Use GridBagLayout
+
+ // contentTypeLabel with fixed width
+ contentTypePanel.add(contentTypeLabel, GridBagConstraints().apply {
+ gridx = 0
+ gridy = 0
+ weightx = 0.0 // No extra space
+ fill = GridBagConstraints.NONE
+ })
- private lateinit var topPanel: JPanel
+ // contentTypeComboBox to fill the rest
+ contentTypePanel.add(contentTypeComboBox, GridBagConstraints().apply {
+ gridx = 1
+ weightx = 1.0 // Take up remaining space
+ fill = GridBagConstraints.HORIZONTAL
+ })
- private lateinit var callButton: JButton
- private lateinit var requestBodyTextArea: JTextArea
- private lateinit var responseTextArea: JTextArea
- private lateinit var pathTextField: JTextField
+ rightPanel.add(contentTypePanel, GridBagConstraints().apply {
+ fill = GridBagConstraints.HORIZONTAL
+ gridy = 2
+ weightx = 1.0
+ anchor = GridBagConstraints.LINE_START // Align to the left
+ insets = JBUI.insetsLeft(8) // Add a 15-pixel margin to the left
+ })
- private lateinit var methodLabel: JLabel
- private lateinit var requestPanel: JBTabbedPane
- private lateinit var requestBodyPanel: JPanel
- private lateinit var requestHeaderPanel: JPanel
- private lateinit var formTable: JBTable
+ // Request Panel within Right Panel
+ requestBodyTextArea = JTextArea()
+ val requestBodyPanel = JScrollPane(requestBodyTextArea)
- private lateinit var responsePanel: JBTabbedPane
- private lateinit var formatOrRawButton: JButton
- private lateinit var saveButton: JButton
- private lateinit var responseActionPanel: JPanel
- private lateinit var responseHeadersTextArea: JTextArea
- private lateinit var requestHeadersTextArea: JTextArea
- private lateinit var statusLabel: JLabel
+ formTable = JBTable()
+ val formPanel = JScrollPane(formTable)
- private lateinit var paramPanel: JPanel
- private lateinit var paramsLabel: JLabel
- private lateinit var paramsTextField: JTextField
+ requestHeadersTextArea = JTextArea()
+ val requestHeaderPanel = JScrollPane(requestHeadersTextArea)
- private lateinit var contentTypePanel: JPanel
- private lateinit var contentTypeLabel: JLabel
- private lateinit var contentTypeComboBox: JComboBox
+ requestPanel = JBTabbedPane().apply {
+ addTab("Body", requestBodyPanel)
+ addTab("Form", formPanel)
+ addTab("Headers", requestHeaderPanel)
+ }
+
+ rightPanel.add(requestPanel, GridBagConstraints().apply {
+ fill = GridBagConstraints.BOTH
+ gridy = 3
+ weightx = 1.0
+ weighty = 1.0
+ })
+
+ // Response Panel within Right Panel
+ responseTextArea = JTextArea()
+ formatOrRawButton = JButton("format")
+ statusLabel = JBLabel("status: unknown")
+ saveButton = JButton("save")
+ responseActionPanel = JPanel().apply {
+ layout = BoxLayout(this, BoxLayout.Y_AXIS)
+ add(formatOrRawButton)
+ add(statusLabel)
+ add(saveButton)
+ // Add a vertical glue to push all components to the top
+ add(Box.createVerticalGlue())
+ }
+
+
+ val responseBodyPanel = JPanel(GridBagLayout()).apply {
+ add(JScrollPane(responseTextArea), GridBagConstraints().apply {
+ fill = GridBagConstraints.BOTH
+ gridx = 0
+ weightx = 1.0
+ weighty = 1.0
+ })
+ add(responseActionPanel, GridBagConstraints().apply {
+ fill = GridBagConstraints.VERTICAL
+ gridx = 1
+ weightx = 0.1
+ weighty = 1.0
+ anchor = GridBagConstraints.NORTH
+ })
+ }
+
+ responseHeadersTextArea = JTextArea().apply { isEditable = false }
+ val responseHeaderPanel = JScrollPane(responseHeadersTextArea)
+
+ responsePanel = JBTabbedPane().apply {
+ addTab("Body", responseBodyPanel)
+ addTab("Headers", responseHeaderPanel)
+ }
+ rightPanel.add(responsePanel, GridBagConstraints().apply {
+ fill = GridBagConstraints.BOTH
+ gridy = 4
+ weightx = 1.0
+ weighty = 1.0
+ })
+ }
+
+}
+
+class ApiCallDialog : ContextDialog(), ApiCallUI {
+ private val apiCallPanel = ApiCallPanel()
+
+ private val searchTextField: JTextField
+ get() = apiCallPanel.searchTextField
+
+ private val apisJList: JList
+ get() = apiCallPanel.apisJList
+
+ private val callButton: JButton
+ get() = apiCallPanel.callButton
+ private val requestBodyTextArea: JTextArea
+ get() = apiCallPanel.requestBodyTextArea
+ private val responseTextArea: JTextArea
+ get() = apiCallPanel.responseTextArea
+ private val pathTextField: JTextField
+ get() = apiCallPanel.pathTextField
+
+ private val methodLabel: JLabel
+ get() = apiCallPanel.methodLabel
+ private val requestPanel: JBTabbedPane
+ get() = apiCallPanel.requestPanel
+ private val formTable: JBTable
+ get() = apiCallPanel.formTable
+
+ private val formatOrRawButton: JButton
+ get() = apiCallPanel.formatOrRawButton
+ private val saveButton: JButton
+ get() = apiCallPanel.saveButton
+ private val responseActionPanel: JPanel
+ get() = apiCallPanel.responseActionPanel
+
+ private val responseHeadersTextArea: JTextArea
+ get() = apiCallPanel.responseHeadersTextArea
+ private val requestHeadersTextArea: JTextArea
+ get() = apiCallPanel.requestHeadersTextArea
+ private val statusLabel: JLabel
+ get() = apiCallPanel.statusLabel
+
+ private val paramPanel: JPanel
+ get() = apiCallPanel.paramPanel
+ private val paramsLabel: JLabel
+ get() = apiCallPanel.paramsLabel
+ private val paramsTextField: JTextField
+ get() = apiCallPanel.paramsTextField
+
+ private val contentTypePanel: JPanel
+ get() = apiCallPanel.contentTypePanel
+ private val contentTypeLabel: JLabel
+ get() = apiCallPanel.contentTypeLabel
+ private val contentTypeComboBox: JComboBox
+ get() = apiCallPanel.contentTypeComboBox
+
+ private val hostComboBox: JComboBox
+ get() = apiCallPanel.hostComboBox
// private val autoComputer: AutoComputer = AutoComputer()
@@ -98,11 +378,9 @@ class ApiCallDialog : ContextDialog(), ApiCallUI {
private var currUrl: String? = null
- private var hostComboBox: JComboBox? = null
private val requestRawInfoBinderFactory: DbBeanBinderFactory by lazy {
- DbBeanBinderFactory(projectCacheRepository!!.getOrCreateFile(".api.call.v1.0.db").path)
- { NULL_REQUEST_INFO_CACHE }
+ DbBeanBinderFactory(projectCacheRepository!!.getOrCreateFile(".api.call.v1.0.db").path) { NULL_REQUEST_INFO_CACHE }
}
private val throttleHelper = ThrottleHelper()
@@ -113,7 +391,13 @@ class ApiCallDialog : ContextDialog(), ApiCallUI {
init {
LOG.info("create ApiCallDialog")
- setContentPane(contentPane)
+
+ // Setting preferred, maximum, and minimum sizes
+ this.preferredSize = Dimension(900, 600)
+ this.maximumSize = Dimension(1200, 800)
+ this.minimumSize = Dimension(600, 400)
+
+ contentPane = apiCallPanel
getRootPane().defaultButton = callButton
contentTypeComboBox.model = DefaultComboBoxModel(CONTENT_TYPES)
@@ -127,14 +411,12 @@ class ApiCallDialog : ContextDialog(), ApiCallUI {
})
// call onCancel() on ESCAPE
- contentPane.registerKeyboardAction(
- { onCancel() },
- KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
- JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
+ apiCallPanel.registerKeyboardAction(
+ { onCancel() }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
)
// call onCallClick() on ENTER
- contentPane.registerKeyboardAction(
+ apiCallPanel.registerKeyboardAction(
{ onCallClick() },
KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0),
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
@@ -175,11 +457,17 @@ class ApiCallDialog : ContextDialog(), ApiCallUI {
formatOrRawButton.isFocusPainted = false
saveButton.isFocusPainted = false
- SwingUtils.underLine(this.hostComboBox!!)
+ SwingUtils.underLine(this.hostComboBox)
SwingUtils.underLine(this.pathTextField)
SwingUtils.underLine(this.paramsTextField)
EasyIcons.Run.iconOnly(this.callButton)
+
+ SearchSupport.bindSearch(
+ searchInputField = searchTextField,
+ sourceList = { apiModelList },
+ uiList = apisJList
+ )
}
override fun init() {
@@ -201,7 +489,7 @@ class ApiCallDialog : ContextDialog(), ApiCallUI {
}
}
- apisPopMenu = JPopupMenu()
+ val apisPopMenu = JPopupMenu()
val resetItem = JMenuItem("Reset")
@@ -237,10 +525,12 @@ class ApiCallDialog : ContextDialog(), ApiCallUI {
this.contentTypeComboBox.selectedItem = currRequest.contentType()
updateResponse(null)
this.responseTextArea.text =
- apiList?.get(selectedIndex)?.origin
- ?.response?.firstOrNull()?.body?.let { RequestUtils.parseRawBody(it) }
+ apiList?.get(selectedIndex)?.origin?.response?.firstOrNull()?.body?.let { RequestUtils.parseRawBody(it) }
?: ""
formatForm(currRequest)
+
+ apiCallPanel.revalidate()
+ apiCallPanel.repaint()
}
override fun updateRequestList(requestList: List?) {
@@ -248,26 +538,29 @@ class ApiCallDialog : ContextDialog(), ApiCallUI {
return
}
doAfterInit {
- val requestRawList = ArrayList(requestList.size)
- val requestRawViewList = ArrayList(requestList.size)
- requestList.forEach { request ->
- val rawInfo = rawInfo(request)
- requestRawList.add(ApiInfo(request, rawInfo))
- val beanBinder = this.requestRawInfoBinderFactory.getBeanBinder(rawInfo.cacheKey())
- requestRawViewList.add(beanBinder.tryRead() ?: rawInfo.copy())
- }
- this.apiList = requestRawList
- this.requestRawViewList = requestRawViewList
- this.apisJList.model = DefaultComboBoxModel(
- List(requestRawViewList.size) { index: Int -> RequestNameWrapper(index) }
- .toTypedArray()
- )
- if (requestRawViewList.isNotEmpty()) {
- this.apisJList.selectedIndex = 0
+ actionContext.runInSwingUI {
+ val requestRawList = ArrayList(requestList.size)
+ val requestRawViewList = ArrayList(requestList.size)
+ requestList.forEach { request ->
+ val rawInfo = rawInfo(request)
+ requestRawList.add(ApiInfo(request, rawInfo))
+ val beanBinder = this.requestRawInfoBinderFactory.getBeanBinder(rawInfo.cacheKey())
+ requestRawViewList.add(beanBinder.tryRead() ?: rawInfo.copy())
+ }
+ this.apiList = requestRawList
+ this.requestRawViewList = requestRawViewList
+ this.apisJList.model = CollectionListModel(apiModelList)
+ if (requestRawViewList.isNotEmpty()) {
+ this.apisJList.selectedIndex = 0
+ }
}
}
}
+ private val apiModelList: List
+ get() = requestRawViewList?.let { List(it.size) { index: Int -> RequestNameWrapper(index) } }
+ ?: emptyList()
+
private fun resetCurrentRequestView() {
val index = this.apisJList.selectedIndex
if (index < 0) {
@@ -278,8 +571,7 @@ class ApiCallDialog : ContextDialog(), ApiCallUI {
val requestView = requestRawInfo.copy()
(this.requestRawViewList as MutableList)[index] = requestView
changeRequest()
- requestRawInfoBinderFactory.getBeanBinder(requestRawInfo.cacheKey())
- .save(null)
+ requestRawInfoBinderFactory.getBeanBinder(requestRawInfo.cacheKey()).save(null)
this.apisJList.repaint()
}
@@ -306,12 +598,9 @@ class ApiCallDialog : ContextDialog(), ApiCallUI {
httpClient = httpClientProvider!!.getHttpClient()
try {
- httpContextCacheHelper.getCookies()
- .asSequence()
- .filter { it.getName().notNullOrEmpty() }
- .forEach {
- httpClient!!.cookieStore().addCookie(it)
- }
+ httpContextCacheHelper.getCookies().asSequence().filter { it.getName().notNullOrEmpty() }.forEach {
+ httpClient!!.cookieStore().addCookie(it)
+ }
} catch (e: Exception) {
logger.traceError("load cookie failed!", e)
}
@@ -321,24 +610,20 @@ class ApiCallDialog : ContextDialog(), ApiCallUI {
private fun initRequestModule() {
- (this.requestPanel.getTabComponentAt(0) as? JLabel)
- ?.listenModify(requestBodyTextArea) {
- selectedRequestRawInfo()?.body
- }
+ (this.requestPanel.getTabComponentAt(0) as? JLabel)?.listenModify(requestBodyTextArea) {
+ selectedRequestRawInfo()?.body
+ }
- (this.requestPanel.getTabComponentAt(2) as? JLabel)
- ?.listenModify(requestHeadersTextArea) {
- selectedRequestRawInfo()?.headers
- }
+ (this.requestPanel.getTabComponentAt(2) as? JLabel)?.listenModify(requestHeadersTextArea) {
+ selectedRequestRawInfo()?.headers
+ }
this.paramsLabel.listenModify(this.paramsTextField) {
selectedRequestRawInfo()?.querys
}
this.requestHeadersTextArea.onTextChange { header ->
- if (contentTypeChangeThrottle.acquire(500)
- && requestChangeThrottle.acquire(100)
- ) {
+ if (contentTypeChangeThrottle.acquire(500) && requestChangeThrottle.acquire(100)) {
this.currRequest?.headers = header
this.currRequest?.contentType()?.let {
if (this.contentTypeComboBox.selectedItem != it) {
@@ -400,20 +685,15 @@ class ApiCallDialog : ContextDialog(), ApiCallUI {
return header
} else {
found = true
- newHeader.appendlnIfNotEmpty()
- .append("Content-Type=")
- .append(contentType)
+ newHeader.appendlnIfNotEmpty().append("Content-Type=").append(contentType)
}
} else {
- newHeader.appendlnIfNotEmpty()
- .append(line)
+ newHeader.appendlnIfNotEmpty().append(line)
}
}
}
if (!found) {
- newHeader.appendlnIfNotEmpty()
- .append("Content-Type=")
- .append(contentType)
+ newHeader.appendlnIfNotEmpty().append("Content-Type=").append(contentType)
}
return newHeader.toString()
}
@@ -423,8 +703,7 @@ class ApiCallDialog : ContextDialog(), ApiCallUI {
if (request.querys.isNullOrEmpty()) {
return ""
}
- val path = StringBuilder()
- .append("?")
+ val path = StringBuilder().append("?")
request.querys!!.forEach { param ->
if (path.lastOrNull() != '?') {
path.append("&")
@@ -816,12 +1095,10 @@ class ApiCallDialog : ContextDialog(), ApiCallUI {
if (apiCallDialog!!.throttleHelper.acquire("select_file_for_form_param", 1000)) {
IdeaFileChooserHelper.create(
- apiCallDialog!!.actionContext,
- FileChooserDescriptorFactory.createSingleFileDescriptor()
- ).lastSelectedLocation("file.form.param.select.last.location.key")
- .selectFile {
- formTable.setValueAt(it?.path, row, column)
- }
+ apiCallDialog!!.actionContext, FileChooserDescriptorFactory.createSingleFileDescriptor()
+ ).lastSelectedLocation("file.form.param.select.last.location.key").selectFile {
+ formTable.setValueAt(it?.path, row, column)
+ }
}
formTable.selectionModel.clearSelection()
}
@@ -842,10 +1119,7 @@ class ApiCallDialog : ContextDialog(), ApiCallUI {
if (request?.headers.isNullOrEmpty()) return ""
val sb = StringBuilder()
request?.headers?.forEach {
- sb.append(it.name)
- .append("=")
- .append(it.value ?: "")
- .appendLine()
+ sb.append(it.name).append("=").append(it.value ?: "").appendLine()
}
return sb.toString()
}
@@ -859,7 +1133,7 @@ class ApiCallDialog : ContextDialog(), ApiCallUI {
actionContext.withBoundary {
val hosts = httpContextCacheHelper.getHosts()
actionContext.runInSwingUI {
- this.hostComboBox!!.model = DefaultComboBoxModel(hosts.toTypedArray())
+ this.hostComboBox.model = DefaultComboBoxModel(hosts.toTypedArray())
}
}
}
@@ -871,7 +1145,7 @@ class ApiCallDialog : ContextDialog(), ApiCallUI {
}
refreshDataFromUI()
val request = currRequest!!
- val host = this.hostComboBox!!.editor.item as String
+ val host = this.hostComboBox.editor.item as String
val path = this.pathTextField.text
val query = this.paramsTextField.text
@@ -882,12 +1156,9 @@ class ApiCallDialog : ContextDialog(), ApiCallUI {
onNewHost(host)
var url: String? = null
try {
- url = RequestUtils.UrlBuild().host(host)
- .path(path)
- .query(query).url()
+ url = RequestUtils.UrlBuild().host(host).path(path).query(query).url()
this.currUrl = url
- val httpRequest = getHttpClient().request().method(request.method ?: "GET")
- .url(url)
+ val httpRequest = getHttpClient().request().method(request.method ?: "GET").url(url)
if (requestHeader.notNullOrBlank()) {
parseEqualLine(requestHeader) { name, value ->
@@ -921,8 +1192,7 @@ class ApiCallDialog : ContextDialog(), ApiCallUI {
}
}
if (request.body != null) {
- httpRequest.contentType(ContentType.APPLICATION_JSON)
- .body(requestBodyOrForm)
+ httpRequest.contentType(ContentType.APPLICATION_JSON).body(requestBodyOrForm)
}
}
@@ -935,13 +1205,14 @@ class ApiCallDialog : ContextDialog(), ApiCallUI {
} catch (e: Exception) {
actionContext.runInSwingUI {
- responseTextArea.text = "Could not get any response" +
- "\nThere was an error connecting:" + url +
- "\nThe stackTrace is:" +
- ExceptionUtils.getStackTrace(e)
+ responseTextArea.text =
+ "Could not get any response" + "\nThere was an error connecting:" + url + "\nThe stackTrace is:" + ExceptionUtils.getStackTrace(
+ e
+ )
}
}
}
+
}
//endregion
@@ -964,10 +1235,7 @@ class ApiCallDialog : ContextDialog(), ApiCallUI {
if (response?.headers() == null) return ""
val sb = StringBuilder()
response.headers()?.forEach {
- sb.append(it.name())
- .append("=")
- .append(it.value())
- .appendLine()
+ sb.append(it.name()).append("=").append(it.value()).appendLine()
}
return sb.toString()
}
@@ -981,8 +1249,7 @@ class ApiCallDialog : ContextDialog(), ApiCallUI {
refreshDataFromUI()
if (this.currResponse == null) {
Messages.showMessageDialog(
- this, "No Response",
- "Error", Messages.getErrorIcon()
+ this, "No Response", "Error", Messages.getErrorIcon()
)
return
}
@@ -991,8 +1258,7 @@ class ApiCallDialog : ContextDialog(), ApiCallUI {
val bytes = response.bytes()
if (bytes == null) {
Messages.showMessageDialog(
- this, "Response is empty",
- "Error", Messages.getErrorIcon()
+ this, "Response is empty", "Error", Messages.getErrorIcon()
)
return
}
@@ -1042,8 +1308,7 @@ class ApiCallDialog : ContextDialog(), ApiCallUI {
httpClient?.cookieStore()?.cookies()?.let { httpContextCacheHelper.addCookies(it) }
this.requestRawViewList?.forEachIndexed { index, requestRawInfo ->
if (requestRawInfo != this.apiList!![index].raw) {
- this.requestRawInfoBinderFactory.getBeanBinder(requestRawInfo.cacheKey())
- .save(requestRawInfo)
+ this.requestRawInfoBinderFactory.getBeanBinder(requestRawInfo.cacheKey()).save(requestRawInfo)
}
}
(httpClient as? Closeable)?.close()
@@ -1077,9 +1342,7 @@ class ApiCallDialog : ContextDialog(), ApiCallUI {
return try {
val contentType = safe { response.contentType()?.let { ContentType.parse(it) } }
if (contentType != null) {
- if (contentType.mimeType.startsWith("text/html") ||
- contentType.mimeType.startsWith("text/xml")
- ) {
+ if (contentType.mimeType.startsWith("text/html") || contentType.mimeType.startsWith("text/xml")) {
val doc: Document = Jsoup.parse(getRawResult())
doc.outputSettings().prettyPrint(true)
return doc.outerHtml()
@@ -1169,12 +1432,9 @@ class ApiCallDialog : ContextDialog(), ApiCallUI {
if (this.headers.isNullOrEmpty()) {
return null
}
- val lowerName = name.toLowerCase()
- return parseEqualLine(this.headers!!) { k, v -> k to v }
- .asSequence()
- .filter { it.first.toLowerCase() == lowerName }
- .map { it.second }
- .firstOrNull()
+ val lowerName = name.lowercase()
+ return parseEqualLine(this.headers!!) { k, v -> k to v }.asSequence()
+ .filter { it.first.lowercase() == lowerName }.map { it.second }.firstOrNull()
}
fun copy(): RequestRawInfo {
@@ -1244,8 +1504,7 @@ class ApiCallDialog : ContextDialog(), ApiCallUI {
val requestRawInfo = RequestRawInfo()
requestRawInfo.key = actionContext.callInReadUI {
PsiClassUtils.fullNameOfMember(
- request.resourceClass(),
- request.resourceMethod()!!
+ request.resourceClass(), request.resourceMethod()!!
)
}
requestRawInfo.name = request.name?.trim()
@@ -1262,11 +1521,7 @@ class ApiCallDialog : ContextDialog(), ApiCallUI {
companion object : Log() {
var CONTENT_TYPES: Array = arrayOf(
- "",
- "application/json",
- "application/x-www-form-urlencoded",
- "multipart/form-data",
- "application/xml"
+ "", "application/json", "application/x-www-form-urlencoded", "multipart/form-data", "application/xml"
)
val disabledFormTableBinder: FormTableBinder = DisabledFormTableBinder()
diff --git a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/dialog/ChooseWithTipDialog.form b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/dialog/ChooseWithTipDialog.form
deleted file mode 100644
index f7aaaf0b9..000000000
--- a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/dialog/ChooseWithTipDialog.form
+++ /dev/null
@@ -1,96 +0,0 @@
-
-
diff --git a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/dialog/ChooseWithTipDialog.kt b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/dialog/ChooseWithTipDialog.kt
index 94372c0d7..d6e9b2326 100644
--- a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/dialog/ChooseWithTipDialog.kt
+++ b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/dialog/ChooseWithTipDialog.kt
@@ -1,61 +1,87 @@
package com.itangcent.idea.plugin.dialog
import com.intellij.ui.MutableCollectionComboBoxModel
+import com.intellij.ui.components.JBScrollPane
import com.itangcent.common.utils.notNullOrEmpty
import com.itangcent.idea.utils.SwingUtils
-import java.awt.EventQueue
-import java.awt.Window
+import java.awt.*
import java.awt.event.KeyEvent
import java.awt.event.WindowAdapter
import java.awt.event.WindowEvent
import javax.swing.*
+import kotlin.math.max
class ChooseWithTipDialog(owner: Window? = null) : JDialog(owner) {
- private var contentPane: JPanel? = null
- private var messageLabel: JLabel? = null
- private var buttonOK: JButton? = null
- private var buttonCancel: JButton? = null
- private var itemComboBox: JComboBox? = null
- private var itemTip: JTextArea? = null
- private var items: List? = null
- private var tipAs: ((T) -> String?)? = null
- private var callBack: ((T?) -> Unit)? = null
+
+ private val contentPane = JPanel(BorderLayout())
+ private val messageLabel = JLabel("Label")
+ private val itemComboBox = JComboBox()
+ private val itemTip = JTextArea()
+ private val buttonOK = JButton("OK")
+ private val buttonCancel = JButton("Cancel")
+
+ private var items: List = emptyList()
+ private var tipAs: ((T) -> String) = { it.toString() }
+ private var callBack: ((T?) -> Unit) = {}
fun updateItems(
message: String?,
- items: List?,
+ items: List,
showAs: ((T) -> String?)?,
- tipAs: ((T) -> String?)?,
+ tipAs: ((T) -> String)?,
callBack: ((T?) -> Unit),
) {
this.items = items
- this.tipAs = tipAs
+ if (tipAs != null) {
+ this.tipAs = tipAs
+ }
this.callBack = callBack
- val showValues = items?.map(showAs ?: { it.toString() }) ?: emptyList()
+ val showValues = items.map(showAs ?: { it.toString() })
EventQueue.invokeLater {
if (message.isNullOrBlank()) {
- messageLabel!!.isVisible = false
+ messageLabel.isVisible = false
} else {
- messageLabel!!.text = message
+ messageLabel.text = message
}
if (showValues.notNullOrEmpty()) {
- itemComboBox!!.model =
+ itemComboBox.model =
MutableCollectionComboBoxModel(showValues)
- itemComboBox!!.selectedIndex = 0
+ itemComboBox.selectedIndex = 0
}
}
+
+ val widthForShowItems = showValues.asSequence()
+ .filterNotNull()
+ .map { it.length }
+ .maxOrNull()?.let { it * 10 } ?: 0
+ val tips = items.map(this.tipAs).map { it.split("\n") }
+ val heightForTips = tips.asSequence()
+ .map { it.size }
+ .maxOrNull()?.let { it * 20 } ?: 0
+ val widthForTips = tips.asSequence()
+ .flatMap { it.asSequence() }
+ .map { it.length }
+ .maxOrNull()?.let { it * 10 } ?: 0
+ EventQueue.invokeLater {
+ this.size = Dimension(
+ max(widthForShowItems, widthForTips)
+ .coerceIn(200, 500),
+ heightForTips
+ .coerceIn(200, 500)
+ )
+ }
}
private fun onOK() {
- val selectedItem = itemComboBox!!.selectedIndex.takeIf { it != -1 }
- ?.let { items!![it] }
+ val selectedItem = itemComboBox.selectedIndex.takeIf { it != -1 }
+ ?.let { items[it] }
dispose()
- callBack!!(selectedItem)
+ callBack(selectedItem)
}
private fun onCancel() {
dispose()
- callBack!!(null)
+ callBack(null)
}
private fun close() {
@@ -63,13 +89,36 @@ class ChooseWithTipDialog(owner: Window? = null) : JDialog(owner) {
}
init {
+ // Top Section
+ contentPane.add(messageLabel, BorderLayout.NORTH)
+
+ // Middle Section
+ val middlePanel = JPanel(BorderLayout())
+ middlePanel.add(itemComboBox, BorderLayout.NORTH)
+ val scrollPane = JBScrollPane(itemTip)
+ middlePanel.add(scrollPane, BorderLayout.CENTER)
+ contentPane.add(middlePanel, BorderLayout.CENTER)
+
+ // Bottom Section
+ val bottomPanel = JPanel(FlowLayout(FlowLayout.RIGHT))
+ bottomPanel.add(buttonOK)
+ bottomPanel.add(buttonCancel)
+ contentPane.add(bottomPanel, BorderLayout.SOUTH)
+
+ pack()
+ setLocationRelativeTo(null) // Center the dialog on the screen
+
+ // Action Listeners
+ buttonOK.addActionListener { onOK() }
+ buttonCancel.addActionListener { onCancel() }
+
setContentPane(contentPane)
isModal = true
getRootPane().defaultButton = buttonOK
SwingUtils.centerWindow(this)
- buttonOK!!.addActionListener { onOK() }
- buttonCancel!!.addActionListener { onCancel() }
+ buttonOK.addActionListener { onOK() }
+ buttonCancel.addActionListener { onCancel() }
// call onCancel() when cross is clicked
defaultCloseOperation = DO_NOTHING_ON_CLOSE
@@ -80,24 +129,27 @@ class ChooseWithTipDialog(owner: Window? = null) : JDialog(owner) {
})
// call onCancel() on ESCAPE
- contentPane!!.registerKeyboardAction({ onCancel() },
+ contentPane.registerKeyboardAction(
+ { onCancel() },
KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
- JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
+ )
- itemComboBox?.addActionListener {
+ itemComboBox.addActionListener {
onItemSelected()
}
}
private fun onItemSelected() {
- val tip = itemComboBox!!.selectedIndex.takeIf { it != -1 }
- .takeIf { tipAs != null }
- ?.let { items!![it] }?.let { tipAs!!(it) }
+ val tip = itemComboBox.selectedIndex.takeIf { it != -1 }
+ ?.let { items[it] }
+ ?.let { tipAs(it) }
if (tip.isNullOrBlank()) {
- itemTip!!.isVisible = false
+ itemTip.isVisible = false
+ itemTip.text = ""
return
}
- itemTip!!.isVisible = true
- itemTip!!.text = tip
+ itemTip.isVisible = true
+ itemTip.text = tip
}
}
\ No newline at end of file
diff --git a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/dialog/SearchSupport.kt b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/dialog/SearchSupport.kt
new file mode 100644
index 000000000..36a814433
--- /dev/null
+++ b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/dialog/SearchSupport.kt
@@ -0,0 +1,102 @@
+package com.itangcent.idea.plugin.dialog
+
+import com.intellij.ui.CollectionListModel
+import javax.swing.JList
+import javax.swing.JTextField
+import javax.swing.Timer
+import javax.swing.event.DocumentEvent
+import javax.swing.event.DocumentListener
+
+/**
+ * @author joe.wu
+ * @date 2023/11/13 7:58am
+ */
+object SearchSupport {
+
+ val BASIC_MATCHER: Matcher = { search, item ->
+ item.toString().contains(search, true)
+ }
+
+ val ENHANCED_MATCHER: Matcher = { search, item ->
+ isSubsequence(search, item.toString())
+ }
+
+ private fun isSubsequence(s: String, t: String): Boolean {
+ var indexS = 0
+ var indexT = 0
+ while (indexS < s.length && indexT < t.length) {
+ if (s[indexS].equals(t[indexT], ignoreCase = true)) {
+ indexS++
+ }
+ indexT++
+ }
+ return indexS == s.length
+ }
+
+ @Suppress("SYNTHETIC_SETTER_PROJECTED_OUT")
+ fun bindSearch(
+ searchInputField: JTextField,
+ sourceList: () -> List<*>,
+ uiList: JList<*>,
+ match: (String, Any) -> Boolean = ENHANCED_MATCHER,
+ onSearch: (String) -> Unit = {}
+ ) {
+ // Keep track of the previous text to avoid unnecessary updates
+ var previousText: String? = null
+
+ fun updateList() {
+ val source = sourceList()
+ val query = searchInputField.text
+
+ if (query == previousText) {
+ return
+ }
+
+ previousText = query
+
+ // Remember the currently selected items
+ val selectedItems = uiList.selectedValuesList
+
+ val newModel = if (query.isNullOrEmpty()) {
+ CollectionListModel(source)
+ } else {
+ val filtered = source.asSequence().filterNotNull().filter { match(query, it) }.toList()
+ CollectionListModel(filtered)
+ }
+
+ uiList.model = newModel
+
+ // Restore the selection
+ val indicesToSelect = mutableListOf()
+ for (selectedItem in selectedItems) {
+ val index = newModel.items.indexOf(selectedItem)
+ if (index != -1) {
+ indicesToSelect.add(index)
+ }
+ }
+
+ val selectedIndices = indicesToSelect.toIntArray()
+ uiList.selectedIndices = selectedIndices
+
+ onSearch(query)
+ }
+
+ val timer = Timer(600) { updateList() }
+
+ searchInputField.document.addDocumentListener(object : DocumentListener {
+ override fun insertUpdate(e: DocumentEvent) {
+ timer.restart()
+ }
+
+ override fun removeUpdate(e: DocumentEvent) {
+ timer.restart()
+ }
+
+ override fun changedUpdate(e: DocumentEvent) {
+ timer.restart()
+ }
+ })
+ }
+}
+
+typealias Matcher = (String, Any) -> Boolean
\ No newline at end of file
diff --git a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/dialog/SuvApiExportDialog.form b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/dialog/SuvApiExportDialog.form
deleted file mode 100644
index 62ff91158..000000000
--- a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/dialog/SuvApiExportDialog.form
+++ /dev/null
@@ -1,132 +0,0 @@
-
-
diff --git a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/dialog/SuvApiExportDialog.kt b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/dialog/SuvApiExportDialog.kt
index ccda16a3b..9af8feab3 100644
--- a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/dialog/SuvApiExportDialog.kt
+++ b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/dialog/SuvApiExportDialog.kt
@@ -1,7 +1,10 @@
package com.itangcent.idea.plugin.dialog
import com.intellij.ide.util.PropertiesComponent
+import com.intellij.openapi.ui.ComboBox
import com.intellij.ui.components.JBCheckBox
+import com.intellij.ui.components.JBList
+import com.intellij.util.ui.components.BorderLayoutPanel
import com.itangcent.common.logger.traceError
import com.itangcent.common.utils.GsonUtils
import com.itangcent.common.utils.notNullOrEmpty
@@ -15,112 +18,162 @@ import java.awt.event.WindowEvent
import java.awt.event.WindowFocusListener
import javax.swing.*
+
+private class SuvApiExportPanel : BorderLayoutPanel() {
+
+ val searchInputField = JTextField().apply {
+ minimumSize = Dimension(100, 30)
+ }
+ val selectAllCheckBox = JBCheckBox()
+ val channelComboBox = ComboBox()
+ val buttonOK = JButton("✔").apply {
+ preferredSize = Dimension(40, 30)
+ minimumSize = Dimension(40, 30)
+ }
+ val buttonCancel = JButton("X").apply {
+ preferredSize = Dimension(40, 30)
+ minimumSize = Dimension(40, 30)
+ }
+
+ val apiList = JBList()
+
+ init {
+ // Top Panel
+ val topPanel = JPanel().apply {
+ layout = BoxLayout(this, BoxLayout.X_AXIS)
+ add(searchInputField) // Add search input field at the top and left
+ add(selectAllCheckBox)
+ add(channelComboBox)
+ add(Box.createHorizontalGlue()) // To push the following components to the right
+ add(buttonOK)
+ add(buttonCancel)
+ }
+
+ // Center Panel
+ val centerPanel = JScrollPane(apiList)
+
+ // Setup BorderLayoutPanel
+ addToTop(topPanel)
+ addToCenter(centerPanel)
+ }
+}
+
+
class SuvApiExportDialog : ContextDialog() {
companion object {
private const val LAST_USED_CHANNEL = "com.itangcent.easyapi.suv.last.used.channel"
}
- private var contentPane: JPanel? = null
- private var buttonOK: JButton? = null
- private var buttonCancel: JButton? = null
- private var channelComboBox: JComboBox<*>? = null
+ private var trigger = TriggerSupport()
- private var apiList: JList<*>? = null
- private var docList: List<*>? = null
+ private val suvApiExportPanel = SuvApiExportPanel()
+ private val buttonOK get() = suvApiExportPanel.buttonOK
+ private val buttonCancel get() = suvApiExportPanel.buttonCancel
+ private val channelComboBox get() = suvApiExportPanel.channelComboBox
+ private val apiList get() = suvApiExportPanel.apiList
+ private val selectAllCheckBox get() = suvApiExportPanel.selectAllCheckBox
+ private val searchInputField get() = suvApiExportPanel.searchInputField
- private var selectAllCheckBox: JBCheckBox? = null
+ private var docList: List<*>? = null
private var apisHandle: ((Any?, List<*>) -> Unit)? = null
private var onChannelChanged: ((Any?) -> Unit)? = null
init {
- this.isUndecorated = false
+ minimumSize = Dimension(400, 400)
maximumSize = Dimension(800, 800)
- //this.isResizable = false
- setContentPane(contentPane)
- isModal = false
+ contentPane = suvApiExportPanel
getRootPane().defaultButton = buttonOK
SwingUtils.centerWindow(this)
- buttonOK!!.addActionListener { onOK() }
+ buttonOK.addActionListener { onOK() }
- buttonCancel!!.addActionListener { onCancel() }
+ buttonCancel.addActionListener { onCancel() }
- // call onCancel() when cross is clicked
- defaultCloseOperation = WindowConstants.DO_NOTHING_ON_CLOSE
addWindowListener(object : WindowAdapter() {
override fun windowClosing(e: WindowEvent?) {
onCancel()
}
})
- SwingUtils.immersed(this.channelComboBox!!)
+ SwingUtils.immersed(this.channelComboBox)
EasyIcons.Close.iconOnly(this.buttonCancel)
EasyIcons.OK.iconOnly(this.buttonOK)
// call onCancel() on ESCAPE
- contentPane!!.registerKeyboardAction(
+ suvApiExportPanel.registerKeyboardAction(
{ onCancel() },
KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
)
// call onCallClick() on ENTER
- contentPane!!.registerKeyboardAction(
+ suvApiExportPanel.registerKeyboardAction(
{ onOK() },
KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0),
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
)
- selectAllCheckBox!!.addChangeListener {
+ selectAllCheckBox.addChangeListener {
onSelectedAll()
}
- channelComboBox!!.addActionListener {
- onChannelChanged?.invoke(channelComboBox?.selectedItem)
+ channelComboBox.addActionListener {
+ onChannelChanged?.invoke(channelComboBox.selectedItem)
+ }
+
+ SearchSupport.bindSearch(
+ searchInputField = searchInputField,
+ sourceList = { docList!! },
+ uiList = apiList
+ )
+
+ apiList.addListSelectionListener {
+ this.trigger.withTrigger("onSelect") {
+ selectAllCheckBox.isSelected = apiList.model.size == apiList.selectionModel.selectedItemsCount
+ }
}
}
- private fun onSelectedAll() {
- if (selectAllCheckBox!!.isSelected) {
- apiList!!.selectionModel!!.addSelectionInterval(0, docList!!.size)
+ private fun onSelectedAll() = this.trigger.withTrigger("onSelectAll") {
+ if (selectAllCheckBox.isSelected) {
+ apiList.selectionModel!!.addSelectionInterval(0, docList!!.size)
} else {
- apiList!!.selectionModel!!.clearSelection()
+ apiList.selectionModel!!.clearSelection()
}
}
fun updateRequestList(requestList: List<*>) {
this.docList = requestList
- this.apiList!!.model = DefaultComboBoxModel(requestList.toTypedArray())
+ this.apiList.model = DefaultComboBoxModel(requestList.toTypedArray())
}
fun selectAll() {
- this.selectAllCheckBox!!.isSelected = true
- onSelectedAll()
+ this.selectAllCheckBox.isSelected = true
}
fun selectMethod(api: Any?) {
- this.selectAllCheckBox!!.isSelected = false
+ this.selectAllCheckBox.isSelected = false
this.docList?.indexOf(api)?.let {
- apiList!!.selectedIndex = it
+ apiList.selectedIndex = it
}
}
fun setChannels(channels: List<*>) {
- this.channelComboBox!!.model = DefaultComboBoxModel(channels.toTypedArray())
+ this.channelComboBox.model = DefaultComboBoxModel(channels.toTypedArray())
val lastUsedChannel = PropertiesComponent.getInstance().getValue(LAST_USED_CHANNEL)
if (lastUsedChannel.notNullOrEmpty()) {
channels.firstOrNull { it.toString() == lastUsedChannel }
- ?.let { this.channelComboBox!!.model.selectedItem = it }
+ ?.let { this.channelComboBox.model.selectedItem = it }
}
- onChannelChanged?.let { it(this.channelComboBox?.selectedItem) }
+ onChannelChanged?.let { it(this.channelComboBox.selectedItem) }
}
fun setApisHandle(apisHandle: (Any?, List<*>) -> Unit) {
@@ -141,7 +194,7 @@ class SuvApiExportDialog : ContextDialog() {
if (actionContext.callInSwingUI {
if (!disposed && !this.isFocused) {
- this.apiList!!.requestFocus()
+ this.apiList.requestFocus()
return@callInSwingUI false
} else {
return@callInSwingUI true
@@ -173,8 +226,8 @@ class SuvApiExportDialog : ContextDialog() {
}
private fun onOK() {
- val selectedChannel = this.channelComboBox!!.selectedItem
- val selectedApis = GsonUtils.copy(this.apiList!!.selectedValuesList!!) as List<*>
+ val selectedChannel = this.channelComboBox.selectedItem
+ val selectedApis = GsonUtils.copy(this.apiList.selectedValuesList!!) as List<*>
actionContext.runAsync {
try {
this.apisHandle!!(selectedChannel, selectedApis)
diff --git a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/dialog/TriggerSupport.kt b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/dialog/TriggerSupport.kt
new file mode 100644
index 000000000..e9d6d79c9
--- /dev/null
+++ b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/dialog/TriggerSupport.kt
@@ -0,0 +1,29 @@
+package com.itangcent.idea.plugin.dialog
+
+/**
+ * @author joe.wu
+ * @date 2023/11/14 9:20am
+ */
+class TriggerSupport {
+ private var trigger: String? = null
+
+ fun withTrigger(trigger: String, cancelIfTriggerNotMatch: Boolean = true, action: () -> Unit) {
+ if (this.trigger != null && this.trigger != trigger) {
+ if (cancelIfTriggerNotMatch) {
+ return
+ }
+ action()
+ return
+ }
+ this.trigger = trigger
+ try {
+ action()
+ } finally {
+ this.trigger = null
+ }
+ }
+
+ fun getTrigger(): String? {
+ return trigger
+ }
+}
\ No newline at end of file
diff --git a/idea-plugin/src/main/kotlin/com/itangcent/idea/swing/DefaultMessagesHelper.kt b/idea-plugin/src/main/kotlin/com/itangcent/idea/swing/DefaultMessagesHelper.kt
index ad01cf3bb..232d49e7b 100644
--- a/idea-plugin/src/main/kotlin/com/itangcent/idea/swing/DefaultMessagesHelper.kt
+++ b/idea-plugin/src/main/kotlin/com/itangcent/idea/swing/DefaultMessagesHelper.kt
@@ -126,15 +126,17 @@ class DefaultMessagesHelper : MessagesHelper {
override fun showChooseWithTipDialog(
message: String?,
- items: List?,
- showAs: ((T) -> String?)?,
- tipAs: ((T) -> String?)?,
+ items: List,
+ showAs: ((T) -> String)?,
+ tipAs: ((T) -> String)?,
callBack: ((T?) -> Unit),
) {
actionContext.runInSwingUI {
val chooseWithTipDialog = ChooseWithTipDialog(SwingUtils.preferableWindow())
UIUtils.show(chooseWithTipDialog)
- chooseWithTipDialog.updateItems(message, items, showAs, tipAs, callBack)
+ actionContext.runAsync {
+ chooseWithTipDialog.updateItems(message, items, showAs, tipAs, callBack)
+ }
}
}
diff --git a/idea-plugin/src/main/kotlin/com/itangcent/idea/swing/MessagesHelper.kt b/idea-plugin/src/main/kotlin/com/itangcent/idea/swing/MessagesHelper.kt
index bc5f8781b..471c9f241 100644
--- a/idea-plugin/src/main/kotlin/com/itangcent/idea/swing/MessagesHelper.kt
+++ b/idea-plugin/src/main/kotlin/com/itangcent/idea/swing/MessagesHelper.kt
@@ -54,9 +54,9 @@ interface MessagesHelper {
fun showChooseWithTipDialog(
message: String?,
- items: List?,
- showAs: ((T) -> String?)?,
- tipAs: ((T) -> String?)?,
+ items: List,
+ showAs: ((T) -> String)?,
+ tipAs: ((T) -> String)?,
callBack: ((T?) -> Unit),
)
@@ -74,9 +74,9 @@ interface MessagesHelper {
fun MessagesHelper.showChooseWithTipDialog(
message: String?,
- items: List?,
- showAs: ((T) -> String?)?,
- tipAs: ((T) -> String?)?,
+ items: List,
+ showAs: ((T) -> String)?,
+ tipAs: ((T) -> String)?,
): T? {
val valueHolder = ValueHolder()
this.showChooseWithTipDialog(message, items, showAs, tipAs) {
diff --git a/idea-plugin/src/main/kotlin/com/itangcent/idea/utils/SwingUtils.kt b/idea-plugin/src/main/kotlin/com/itangcent/idea/utils/SwingUtils.kt
index 5e0024d29..62a133cba 100644
--- a/idea-plugin/src/main/kotlin/com/itangcent/idea/utils/SwingUtils.kt
+++ b/idea-plugin/src/main/kotlin/com/itangcent/idea/utils/SwingUtils.kt
@@ -2,6 +2,7 @@ package com.itangcent.idea.utils
import com.intellij.openapi.project.Project
import com.intellij.openapi.wm.WindowManager
+import com.itangcent.common.logger.Log
import com.itangcent.common.utils.cast
import com.itangcent.idea.swing.ActiveWindowProvider
import com.itangcent.intellij.context.ActionContext
@@ -13,7 +14,7 @@ import javax.swing.*
import javax.swing.table.TableColumn
import javax.swing.tree.*
-object SwingUtils {
+object SwingUtils : Log() {
// Sets the focus on the specified Dialog component in the Swing UI thread.
fun focus(dialog: Dialog) {
ActionContext.getContext()!!.runInSwingUI {
@@ -84,6 +85,12 @@ object SwingUtils {
?.let { return it }
return null
}
+
+ fun logComponentDetails(component: JComponent, componentName: String) {
+ val location = component.location
+ val size = component.size
+ LOG.info("$componentName - Location: ($location) Dimensions: (${size.width} x ${size.height})")
+ }
}
// Returns true if the mouse event is a double-click event.