Run configuration editor without writeExternal



I'm trying to create a Run configuration (specifically using AbstractPythonRunConfiguration) that relies on a Rest API of another system to get some data for the script execution. All is good and well until I get to the SettingsEditor implementation.

In the SettingsEditor, I want to let the users choose some values that it gets from the API in a comboBox. At this point, whenever the user selects a different item from the comboBox, the Apply button is still grayed out. After some debugging later I saw the apply button relies on the XML serialization of the run configuration in RunConfiguration#writeExternal(). This post sums it up.

Now, In my situation, I don't want to serialize the user choices. I want these settings to live as long as the IDE is open, and re-entered when the IDE is restarted. The reason is that the data I fetch is dynamic and can change between user sessions, and might contain sensitive data. Serializing it also means writing it to workspace.xml.
But if I don't save the configuration in writeExternal, the Apply button will never be enabled. If I select what I need and click OK, the settings are not saved.

The weird thing is when I use LazyRunConfigurationProducer to create a run configuration, It opens a configuration editor (because some parameters are mandatory in RunConfiguration#CheckConfiguration()), and the configuration I enter there is saved to the run configuration, Because the Apply button in this window is enabled by default, regardless of the state of the configuration. When I try to edit this configuration, again the apply button will never be enabled.

Any idea how I can save the config only to my run configuration instance, without serializing it?

Also, I'm using kotlin UI DSL in the SettingsEditor, maybe there's a way to use DialogPanel#onIsModified() or something else to achieve that?



Comment actions Permalink


Is there any chance to see the code to reproduce it? It will be much easier to see the possibilities.

Comment actions Permalink

Hey Karol,

Sure, Apologies for not putting it in the first place. Code is stripped down a bit

The run configuration currently needs a script path, and a "test case" I pull from the API. The sciprtPath is serialized in writeExternal, while the testCase isn't. In checkConfiguration I check that I have all the details to run the script.

class ProductRunConfiguration(project: Project,
                              factory: ConfigurationFactory
) : AbstractPythonRunConfiguration<ProductRunConfiguration>(project, factory) {
    var scriptPath: String? = null
    var testCase: TestCase? = null

    override fun writeExternal(element: Element) {
        JDOMExternalizerUtil.writeField(element, SCRIPT_PATH, scriptPath)

    override fun checkConfiguration() {
        if (scriptPath == null || testCase == null) {
throw RuntimeConfigurationError("Missing test case")
} } }

And this is my editor:

class ProductRunConfigurationEditor(project: Project, configuration: ProductRunConfiguration) : SettingsEditor<ProductRunConfiguration>() {
    private lateinit var panel: DialogPanel
    private val scriptPathField = textFieldWithBrowseButton(
        "Choose Script",
        fileChooserDescriptor = FileChooserDescriptorFactory.createSingleFileDescriptor(PythonFileType.INSTANCE)
    private lateinit var testCaseCombo : ComboBox<TestCase>
    val testCasesComboBoxModel = MutableCollectionComboBoxModel<TestCase>()

    override fun resetEditorFrom(s: ProductRunConfiguration) {
        scriptPathField.text = s.scriptPath!!
        testCasesComboBoxModel.selectedItem = s.testCase

    override fun applyEditorTo(s: ProductRunConfiguration) {
        s.scriptPath = scriptPathField.text
        s.testCase = testCasesComboBoxModel.selectedItem as? TestCase

    override fun createEditor(): JComponent {
        panel = panel {
            row("Script Path") {
            row("Test Case") {
                testCaseCombo = comboBox(testCasesComboBoxModel, SimpleListCellRenderer.create {
                        label, case, _ -> case.let { label.text = "${case.caseId} - ${case.environment} - ${case.title}" }
        return panel

    private fun getTestCases() {
        // execute SwingWorker to fetch test cases from the API and populate testCaseComboBox

The testCase object is being applied to the run configuration in applyEditorTo, but I don't serialize it in writeExternal, so the apply button state won't change when I change the testCase comboBox. Only when I change scriptPath.

Hope that clarifies my issue!


Please sign in to leave a comment.