mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
kotlin UI DSL variant 2 — more readable
make setOKActionEnabled public init to fix ask password dialog
This commit is contained in:
@@ -19,7 +19,7 @@ import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.ui.ValidationInfo;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.ui.ComponentsKt;
|
||||
import com.intellij.ui.components.ComponentsKt;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -95,6 +95,7 @@ class EnterPasswordComponent {
|
||||
note = "The passwords will be stored in IDE configuration files with weak protection<br>(" + subNote + ").";
|
||||
}
|
||||
|
||||
myPromptLabel = ComponentsKt.htmlComponent("Master password is required to convert saved passwords.<br>" + note, UIUtil.getLabelFont(UIUtil.FontSize.SMALL));
|
||||
myPromptLabel = ComponentsKt
|
||||
.htmlComponent("Master password is required to convert saved passwords.<br>" + note, UIUtil.getLabelFont(UIUtil.FontSize.SMALL));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,9 @@ import com.intellij.ide.passwordSafe.impl.PasswordSafeImpl
|
||||
import com.intellij.openapi.options.ConfigurableBase
|
||||
import com.intellij.openapi.options.ConfigurableUi
|
||||
import com.intellij.openapi.ui.Messages
|
||||
import com.intellij.ui.components.RadioButton
|
||||
import com.intellij.ui.layout.*
|
||||
import com.intellij.ui.layout.CCFlags.*
|
||||
import com.intellij.ui.layout.LCFlags.*
|
||||
import java.awt.Component
|
||||
|
||||
class PasswordSafeConfigurable(private val settings: PasswordSafeSettings) : ConfigurableBase<PasswordSafeConfigurableUi, PasswordSafeSettings>("application.passwordSafe", "Passwords", "reference.ide.settings.password.safe") {
|
||||
override fun getSettings() = settings
|
||||
@@ -49,16 +49,23 @@ class PasswordSafeConfigurableUi : ConfigurableUi<PasswordSafeSettings> {
|
||||
settings.providerType = getProviderType()
|
||||
}
|
||||
|
||||
override fun getComponent() = panel(noGrid, flowY, fillX) {
|
||||
override fun getComponent() = panel {
|
||||
val passwordSafe = PasswordSafe.getInstance() as PasswordSafeImpl
|
||||
|
||||
buttonGroup(saveOnDisk, rememberPasswordsUntilClosing)
|
||||
buttonGroup {
|
||||
row { saveOnDisk() }
|
||||
row { rememberPasswordsUntilClosing() }
|
||||
}
|
||||
|
||||
if (!passwordSafe.isNativeCredentialStoreUsed)
|
||||
button("Clear Passwords", right) {
|
||||
passwordSafe.clearPasswords()
|
||||
Messages.showInfoMessage(this@panel, "Passwords were cleared", "Clear Passwords")
|
||||
}
|
||||
row {
|
||||
right {
|
||||
button("Clear Passwords") { event ->
|
||||
passwordSafe.clearPasswords()
|
||||
Messages.showInfoMessage(event.source as Component, "Passwords were cleared", "Clear Passwords")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getProviderType(): ProviderType {
|
||||
|
||||
@@ -101,11 +101,6 @@ public class BaseAnalysisActionDialog extends DialogWrapper {
|
||||
onScopeRadioButtonPressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOKActionEnabled(boolean isEnabled) {
|
||||
super.setOKActionEnabled(isEnabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JComponent createCenterPanel() {
|
||||
myTitledSeparator.setText(myAnalysisNoon);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2013 JetBrains s.r.o.
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -86,9 +86,4 @@ public class EditConfigurationsDialog extends SingleConfigurableEditor implement
|
||||
public Executor getExecutor() {
|
||||
return myExecutor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOKActionEnabled(boolean isEnabled) {
|
||||
super.setOKActionEnabled(isEnabled);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -97,11 +97,6 @@ public class RunDialog extends DialogWrapper implements RunConfigurable.RunDialo
|
||||
return myCenterPanel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOKActionEnabled(final boolean isEnabled){
|
||||
super.setOKActionEnabled(isEnabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dispose() {
|
||||
myConfigurable.disposeUIResources();
|
||||
|
||||
@@ -25,7 +25,7 @@ inline fun <T> runReadAction(crossinline runnable: () -> T): T = ApplicationMana
|
||||
/**
|
||||
* @exclude Internal use only
|
||||
*/
|
||||
fun <T> invokeAndWaitIfNeed(runnable: () -> T): T {
|
||||
fun <T> invokeAndWaitIfNeed(modalityState: ModalityState? = null, runnable: () -> T): T {
|
||||
val app = ApplicationManager.getApplication()
|
||||
if (app == null) {
|
||||
if (SwingUtilities.isEventDispatchThread()) {
|
||||
@@ -39,7 +39,7 @@ fun <T> invokeAndWaitIfNeed(runnable: () -> T): T {
|
||||
}
|
||||
else {
|
||||
var result: T? = null
|
||||
app.invokeAndWait { result = runnable() }
|
||||
app.invokeAndWait({ result = runnable() }, modalityState ?: ModalityState.defaultModalityState())
|
||||
return result as T
|
||||
}
|
||||
}
|
||||
@@ -213,7 +213,11 @@ public abstract class DialogWrapper {
|
||||
}
|
||||
|
||||
protected DialogWrapper(@Nullable Project project, boolean canBeParent, @NotNull IdeModalityType ideModalityType) {
|
||||
myPeer = createPeer(project, canBeParent, ideModalityType);
|
||||
this(project, null, canBeParent, ideModalityType);
|
||||
}
|
||||
|
||||
protected DialogWrapper(@Nullable Project project, @Nullable Component parentComponent, boolean canBeParent, @NotNull IdeModalityType ideModalityType) {
|
||||
myPeer = parentComponent == null ? createPeer(project, canBeParent, project == null ? IdeModalityType.IDE : ideModalityType) : createPeer(parentComponent, canBeParent);
|
||||
final Window window = myPeer.getWindow();
|
||||
if (window != null) {
|
||||
myResizeListener = new ComponentAdapter() {
|
||||
@@ -1480,7 +1484,11 @@ public abstract class DialogWrapper {
|
||||
return myPeer.isModal();
|
||||
}
|
||||
|
||||
protected void setOKActionEnabled(boolean isEnabled) {
|
||||
public boolean isOKActionEnabled() {
|
||||
return myOKAction.isEnabled();
|
||||
}
|
||||
|
||||
public void setOKActionEnabled(boolean isEnabled) {
|
||||
myOKAction.setEnabled(isEnabled);
|
||||
}
|
||||
|
||||
@@ -1538,10 +1546,6 @@ public abstract class DialogWrapper {
|
||||
return getExitCode() == OK_EXIT_CODE;
|
||||
}
|
||||
|
||||
public boolean isOKActionEnabled() {
|
||||
return myOKAction.isEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return <code>true</code> if and only if visible
|
||||
* @see Component#isVisible
|
||||
@@ -1860,7 +1864,7 @@ public abstract class DialogWrapper {
|
||||
if (info.component != null && info.component.isVisible()) {
|
||||
IdeFocusManager.getInstance(null).requestFocus(info.component, true);
|
||||
}
|
||||
DialogEarthquakeShaker.shake((JDialog)getPeer().getWindow());
|
||||
DialogEarthquakeShaker.shake(getPeer().getWindow());
|
||||
startTrackingValidation();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.openapi.ui
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import java.awt.Component
|
||||
import javax.swing.JComponent
|
||||
|
||||
fun dialog(title: String,
|
||||
centerPanel: JComponent,
|
||||
resizable: Boolean = false,
|
||||
preferedFocusComponent: JComponent? = null,
|
||||
okActionEnabled: Boolean = true,
|
||||
project: Project? = null,
|
||||
parent: Component? = null,
|
||||
ok: (() -> Unit)? = null): DialogBuilder {
|
||||
val builder = if (parent == null) DialogBuilder(project) else DialogBuilder(parent)
|
||||
builder
|
||||
.title(title)
|
||||
.centerPanel(centerPanel)
|
||||
.setPreferredFocusComponent(preferedFocusComponent)
|
||||
builder.resizable(resizable)
|
||||
if (!okActionEnabled) {
|
||||
builder.okActionEnabled(false)
|
||||
}
|
||||
|
||||
if (ok != null) {
|
||||
builder.setOkOperation {
|
||||
if (builder.dialogWrapper.okAction.isEnabled) {
|
||||
ok()
|
||||
builder.dialogWrapper.close(DialogWrapper.OK_EXIT_CODE)
|
||||
}
|
||||
}
|
||||
}
|
||||
return builder
|
||||
}
|
||||
@@ -19,4 +19,6 @@ import com.intellij.openapi.util.text.StringUtil
|
||||
|
||||
fun String?.nullize(nullizeSpaces: Boolean = false): String? = StringUtil.nullize(this, nullizeSpaces)
|
||||
|
||||
fun String.trimMiddle(maxLength: Int): String? = StringUtil.trimMiddle(this, maxLength)
|
||||
fun String.trimMiddle(maxLength: Int): String? = StringUtil.trimMiddle(this, maxLength)
|
||||
|
||||
fun CharArray.nullize() = if (isEmpty()) null else this
|
||||
|
||||
@@ -21,45 +21,41 @@ import com.intellij.credentialStore.Credentials
|
||||
import com.intellij.ide.BrowserUtil
|
||||
import com.intellij.ide.passwordSafe.PasswordSafe
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.ui.DialogBuilder
|
||||
import com.intellij.openapi.ui.dialog
|
||||
import com.intellij.ui.components.JBCheckBox
|
||||
import com.intellij.openapi.ui.DialogWrapper
|
||||
import com.intellij.ui.components.CheckBox
|
||||
import com.intellij.ui.components.dialog
|
||||
import com.intellij.ui.layout.*
|
||||
import com.intellij.ui.layout.CCFlags.*
|
||||
import com.intellij.ui.layout.LCFlags.*
|
||||
import com.intellij.util.io.encodeUrlQueryParameter
|
||||
import java.awt.Component
|
||||
import javax.swing.JPasswordField
|
||||
import javax.swing.JTextField
|
||||
|
||||
@JvmOverloads
|
||||
fun showJetBrainsAccountDialog(parent: Component, project: Project? = null): DialogBuilder {
|
||||
fun showJetBrainsAccountDialog(parent: Component, project: Project? = null): DialogWrapper {
|
||||
val credentials = ErrorReportConfigurable.getCredentials()
|
||||
val userField = JTextField(credentials?.userName)
|
||||
val passwordField = JPasswordField(credentials?.password?.toString())
|
||||
|
||||
// if no user name - never stored and so, defaults to remember. if user name set, but no password, so, previously was stored without password
|
||||
val rememberCheckBox = JBCheckBox(CommonBundle.message("checkbox.remember.password"), credentials?.userName == null || !credentials?.password.isNullOrEmpty())
|
||||
val rememberCheckBox = CheckBox(CommonBundle.message("checkbox.remember.password"), selected = credentials?.userName == null || !credentials?.password.isNullOrEmpty())
|
||||
|
||||
val panel = panel(fillX) {
|
||||
label("Login to JetBrains Account to get notified when the submitted\nexceptions are fixed.", span, wrap)
|
||||
label("Username:")
|
||||
userField(grow, wrap)
|
||||
|
||||
label("Password:")
|
||||
passwordField(grow, wrap)
|
||||
rememberCheckBox(skip, split, grow)
|
||||
link("Forgot password?", wrap, right) {
|
||||
BrowserUtil.browse("https://account.jetbrains.com/forgot-password?username=${userField.text.trim().encodeUrlQueryParameter()}")
|
||||
val panel = panel() {
|
||||
noteRow("Login to JetBrains Account to get notified when the submitted\nexceptions are fixed.")
|
||||
row("Username:") { userField() }
|
||||
row("Password:") { passwordField() }
|
||||
row {
|
||||
rememberCheckBox()
|
||||
right {
|
||||
link("Forgot password?") { BrowserUtil.browse("https://account.jetbrains.com/forgot-password?username=${userField.text.trim().encodeUrlQueryParameter()}") }
|
||||
}
|
||||
}
|
||||
|
||||
note("""Do not have an account? <a href="https://account.jetbrains.com/login">Sign Up</a>""", span, wrap)
|
||||
noteRow("""Do not have an account? <a href="https://account.jetbrains.com/login">Sign Up</a>""")
|
||||
}
|
||||
|
||||
return dialog(
|
||||
title = DiagnosticBundle.message("error.report.title"),
|
||||
centerPanel = panel,
|
||||
preferedFocusComponent = if (credentials?.userName == null) userField else passwordField,
|
||||
panel = panel,
|
||||
focusedComponent = if (credentials?.userName == null) userField else passwordField,
|
||||
project = project,
|
||||
parent = if (parent.isShowing) parent else null) {
|
||||
val userName = userField.text
|
||||
|
||||
+31
-110
@@ -15,144 +15,65 @@
|
||||
*/
|
||||
package com.intellij.ide.passwordSafe.ui;
|
||||
|
||||
import com.intellij.credentialStore.Credentials;
|
||||
import com.intellij.ide.passwordSafe.PasswordSafe;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
import com.intellij.credentialStore.CredentialAttributes;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import static com.intellij.credentialStore.CredentialAttributesKt.CredentialAttributes;
|
||||
|
||||
/**
|
||||
* The generic password dialog. Use it to ask a password from user with option to remember it.
|
||||
*/
|
||||
public class PasswordSafePromptDialog extends DialogWrapper {
|
||||
private final PasswordPromptComponent myComponent;
|
||||
|
||||
private PasswordSafePromptDialog(@Nullable Project project, @NotNull String title, @NotNull PasswordPromptComponent component) {
|
||||
super(project, true);
|
||||
|
||||
setTitle(title);
|
||||
myComponent = component;
|
||||
setResizable(false);
|
||||
init();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JComponent createCenterPanel() {
|
||||
return myComponent.getComponent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getPreferredFocusedComponent() {
|
||||
return myComponent.getPreferredFocusedComponent();
|
||||
}
|
||||
|
||||
public class PasswordSafePromptDialog {
|
||||
/**
|
||||
* Ask password possibly asking password database first. The method could be invoked from any thread. If UI needs to be shown,
|
||||
* the method invokes {@link UIUtil#invokeAndWaitIfNeeded(Runnable)}
|
||||
* @param project the context project
|
||||
* @param title the dialog title
|
||||
* @param message the message describing a resource for which password is asked
|
||||
* @param requestor the password requestor
|
||||
* @param key the password key
|
||||
* @param resetPassword if true, the old password is removed from database and new password will be asked.
|
||||
* @param error the error to show in the dialog @return null if dialog was cancelled or password (stored in database or a entered by user)
|
||||
*/
|
||||
@Nullable
|
||||
public static String askPassword(final Project project,
|
||||
final String title,
|
||||
final String message,
|
||||
@NotNull final Class<?> requestor,
|
||||
final String key,
|
||||
boolean resetPassword, String error) {
|
||||
return askPassword(project, title, message, requestor, key, resetPassword, error, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask password possibly asking password database first. The method could be invoked from any thread. If UI needs to be shown,
|
||||
* the method invokes {@link UIUtil#invokeAndWaitIfNeeded(Runnable)}
|
||||
*
|
||||
* @param title the dialog title
|
||||
* @param message the message describing a resource for which password is asked
|
||||
* @param requestor the password requestor
|
||||
* @param key the password key
|
||||
* @param resetPassword if true, the old password is removed from database and new password will be asked.
|
||||
* @param error the error to show in the dialog
|
||||
* @return null if dialog was cancelled or password (stored in database or a entered by user)
|
||||
*/
|
||||
@Nullable
|
||||
public static String askPassword(final String title,
|
||||
final String message,
|
||||
@NotNull final Class<?> requestor,
|
||||
final String key,
|
||||
boolean resetPassword) {
|
||||
return askPassword(null, title, message, requestor, key, resetPassword, null);
|
||||
public static String askPassword(@Nullable Project project,
|
||||
String title,
|
||||
String message,
|
||||
@NotNull Class<?> requestor,
|
||||
String key,
|
||||
boolean resetPassword,
|
||||
String error) {
|
||||
return CredentialPromtKt.askPassword(project, title, message, CredentialAttributes(requestor, key), resetPassword, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param dialogTitle The dialog title
|
||||
* @param passwordFieldLabel The password field label, describing a resource, for which password is asked
|
||||
* @return null if dialog was cancelled or password (stored in database or a entered by user)
|
||||
*/
|
||||
@Nullable
|
||||
public static String askPassword(@NotNull String dialogTitle, @NotNull String passwordFieldLabel, @NotNull CredentialAttributes credentialAttributes) {
|
||||
return CredentialPromtKt.askPassword(null, dialogTitle, passwordFieldLabel, credentialAttributes);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Ask passphrase possibly asking password database first. The method could be invoked from any thread. If UI needs to be shown,
|
||||
* the method invokes {@link UIUtil#invokeAndWaitIfNeeded(Runnable)}
|
||||
* @param project the context project (might be null)
|
||||
* @param title the dialog title
|
||||
* @param message the message describing a resource for which password is asked
|
||||
* @param requestor the password requestor
|
||||
* @param key the password key
|
||||
* @param project The context project (might be null)
|
||||
* @param dialogTitle The dialog title
|
||||
* @param passwordFieldLabel the message describing a resource for which password is asked
|
||||
* @param resetPassword if true, the old password is removed from database and new password will be asked.
|
||||
* @param error the error to show in the dialog @return null if dialog was cancelled or password (stored in database or a entered by user)
|
||||
* @param error the error to show in the dialog
|
||||
* @return null if dialog was cancelled or password (stored in database or a entered by user)
|
||||
*/
|
||||
@Nullable
|
||||
public static String askPassphrase(final Project project,
|
||||
final String title,
|
||||
final String message,
|
||||
@NotNull final Class<?> requestor,
|
||||
final String key,
|
||||
public static String askPassword(@Nullable Project project,
|
||||
String dialogTitle,
|
||||
@NotNull String passwordFieldLabel,
|
||||
@NotNull CredentialAttributes credentialAttributes,
|
||||
boolean resetPassword,
|
||||
String error) {
|
||||
return askPassword(project, title, message, requestor, key, resetPassword, error, "Passphrase:");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String askPassword(Project project,
|
||||
String title,
|
||||
String message,
|
||||
@NotNull Class<?> requestor,
|
||||
String accountName,
|
||||
boolean resetPassword,
|
||||
String error,
|
||||
String promptLabel) {
|
||||
PasswordSafe ps = PasswordSafe.getInstance();
|
||||
if (resetPassword) {
|
||||
ps.set(CredentialAttributes(requestor, accountName), null);
|
||||
}
|
||||
else {
|
||||
String pw = ps.getPassword(requestor, accountName);
|
||||
if (pw != null) {
|
||||
return pw;
|
||||
}
|
||||
}
|
||||
|
||||
Ref<Credentials> ref = Ref.create();
|
||||
ApplicationManager.getApplication().invokeAndWait(() -> {
|
||||
final PasswordPromptComponent component = new PasswordPromptComponent(ps.isMemoryOnly(), message, false, promptLabel);
|
||||
PasswordSafePromptDialog d = new PasswordSafePromptDialog(project, title, component);
|
||||
|
||||
d.setErrorText(error);
|
||||
if (d.showAndGet()) {
|
||||
Credentials credentials = new Credentials(component.getUserName(), component.getPassword());
|
||||
ref.set(credentials);
|
||||
ps.set(CredentialAttributes(requestor, accountName), credentials, !component.isRememberSelected());
|
||||
}
|
||||
}, ModalityState.any());
|
||||
Credentials credentials = ref.get();
|
||||
return credentials == null ? null : credentials.getPasswordAsString();
|
||||
return CredentialPromtKt.askPassword(project, dialogTitle, passwordFieldLabel, credentialAttributes, resetPassword, error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.ide.passwordSafe.ui
|
||||
|
||||
import com.intellij.CommonBundle
|
||||
import com.intellij.credentialStore.CredentialAttributes
|
||||
import com.intellij.credentialStore.Credentials
|
||||
import com.intellij.ide.passwordSafe.PasswordSafe
|
||||
import com.intellij.openapi.application.ModalityState
|
||||
import com.intellij.openapi.application.invokeAndWaitIfNeed
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.ui.components.CheckBox
|
||||
import com.intellij.ui.components.dialog
|
||||
import com.intellij.ui.layout.*
|
||||
import com.intellij.util.text.nullize
|
||||
import javax.swing.JPasswordField
|
||||
|
||||
@JvmOverloads
|
||||
fun askPassword(project: Project?,
|
||||
dialogTitle: String,
|
||||
passwordFieldLabel: String,
|
||||
attributes: CredentialAttributes,
|
||||
resetPassword: Boolean = false,
|
||||
error: String? = null): String? {
|
||||
val store = PasswordSafe.getInstance()
|
||||
if (resetPassword) {
|
||||
store.set(attributes, null)
|
||||
}
|
||||
else {
|
||||
store.get(attributes)?.getPasswordAsString()?.nullize()?.let {
|
||||
return it
|
||||
}
|
||||
}
|
||||
|
||||
return invokeAndWaitIfNeed(ModalityState.any()) {
|
||||
val passwordField = JPasswordField()
|
||||
val rememberCheckBox = if (store.isMemoryOnly) {
|
||||
null
|
||||
}
|
||||
else {
|
||||
CheckBox(CommonBundle.message("checkbox.remember.password"),
|
||||
selected = true,
|
||||
toolTip = "The password will be stored between application sessions.")
|
||||
}
|
||||
|
||||
val panel = panel {
|
||||
row { label(passwordFieldLabel) }
|
||||
row { passwordField() }
|
||||
rememberCheckBox?.let {
|
||||
row { it() }
|
||||
}
|
||||
}
|
||||
|
||||
if (dialog(dialogTitle, project = project, panel = panel, focusedComponent = passwordField, errorText = error).showAndGet()) {
|
||||
val credentials = Credentials(attributes.userName, passwordField.password.nullize())
|
||||
store.set(attributes, credentials, store.isMemoryOnly || rememberCheckBox!!.isSelected)
|
||||
credentials.getPasswordAsString()
|
||||
}
|
||||
else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2013 JetBrains s.r.o.
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -179,7 +179,7 @@ public class FileSaverDialogImpl extends FileChooserDialogImpl implements FileSa
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setOKActionEnabled(boolean isEnabled) {
|
||||
public void setOKActionEnabled(boolean isEnabled) {
|
||||
//double check. FileChooserFactoryImpl sets enable ok button
|
||||
super.setOKActionEnabled(isFileNameExist());
|
||||
}
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.ui
|
||||
|
||||
import com.intellij.openapi.vcs.changes.issueLinks.LinkMouseListenerBase
|
||||
import com.intellij.util.ui.SwingHelper
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import java.awt.Color
|
||||
import java.awt.Font
|
||||
import java.util.regex.Pattern
|
||||
import javax.swing.JEditorPane
|
||||
|
||||
private val HREF_PATTERN = Pattern.compile("<a(?:\\s+href\\s*=\\s*[\"']([^\"']*)[\"'])?\\s*>([^<]*)</a>")
|
||||
|
||||
private val LINK_TEXT_ATTRIBUTES: SimpleTextAttributes
|
||||
get() = SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, UI.getColor("link.foreground"))
|
||||
|
||||
fun noteComponent(note: String): SimpleColoredComponent {
|
||||
val noteComponent = SimpleColoredComponent()
|
||||
|
||||
val matcher = HREF_PATTERN.matcher(note)
|
||||
var prev = 0
|
||||
if (matcher.find()) {
|
||||
do {
|
||||
if (matcher.start() != prev) {
|
||||
noteComponent.append(note.substring(prev, matcher.start()))
|
||||
}
|
||||
noteComponent.append(matcher.group(2), LINK_TEXT_ATTRIBUTES, SimpleColoredComponent.BrowserLauncherTag(matcher.group(1)))
|
||||
prev = matcher.end()
|
||||
}
|
||||
while (matcher.find())
|
||||
|
||||
LinkMouseListenerBase.installSingleTagOn(noteComponent)
|
||||
}
|
||||
|
||||
if (prev < note.length) {
|
||||
noteComponent.append(note.substring(prev))
|
||||
}
|
||||
|
||||
return noteComponent
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun htmlComponent(text: String = "", font: Font = UIUtil.getLabelFont(), background: Color? = null, foreground: Color? = null, lineWrap: Boolean = false): JEditorPane {
|
||||
val pane = SwingHelper.createHtmlViewer(lineWrap, font, background, foreground)
|
||||
if (!text.isNullOrEmpty()) {
|
||||
pane.text = "<html><head>${UIUtil.getCssFontDeclaration(font, UIUtil.getLabelForeground(), null, null)}</head><body>$text</body></html>"
|
||||
}
|
||||
pane.border = null
|
||||
pane.disabledTextColor = UIUtil.getLabelDisabledForeground()
|
||||
pane.addHyperlinkListener(BrowserHyperlinkListener.INSTANCE)
|
||||
return pane
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.ui.components
|
||||
|
||||
import com.intellij.BundleBase
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.ui.DialogWrapper
|
||||
import com.intellij.openapi.ui.ex.MultiLineLabel
|
||||
import com.intellij.openapi.vcs.changes.issueLinks.LinkMouseListenerBase
|
||||
import com.intellij.ui.BrowserHyperlinkListener
|
||||
import com.intellij.ui.SimpleColoredComponent
|
||||
import com.intellij.ui.SimpleTextAttributes
|
||||
import com.intellij.ui.UI
|
||||
import com.intellij.ui.components.labels.LinkLabel
|
||||
import com.intellij.ui.layout.*
|
||||
import com.intellij.util.ui.SwingHelper
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import java.awt.*
|
||||
import java.util.regex.Pattern
|
||||
import javax.swing.*
|
||||
|
||||
private val HREF_PATTERN = Pattern.compile("<a(?:\\s+href\\s*=\\s*[\"']([^\"']*)[\"'])?\\s*>([^<]*)</a>")
|
||||
|
||||
private val LINK_TEXT_ATTRIBUTES: SimpleTextAttributes
|
||||
get() = SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, UI.getColor("link.foreground"))
|
||||
|
||||
fun Label(text: String, style: UIUtil.ComponentStyle? = null, fontColor: UIUtil.FontColor? = null, bold: Boolean = false): JLabel {
|
||||
val finalText = BundleBase.replaceMnemonicAmpersand(text)
|
||||
val label: JLabel
|
||||
if (fontColor == null) {
|
||||
label = if (finalText.contains('\n')) MultiLineLabel(finalText) else JLabel(finalText)
|
||||
style?.let { UIUtil.applyStyle(it, label) }
|
||||
}
|
||||
else {
|
||||
label = JBLabel(finalText, style ?: UIUtil.ComponentStyle.REGULAR, fontColor)
|
||||
}
|
||||
|
||||
if (bold) {
|
||||
label.font = label.font.deriveFont(Font.BOLD)
|
||||
}
|
||||
|
||||
return label
|
||||
}
|
||||
|
||||
fun Link(text: String, style: UIUtil.ComponentStyle? = null, action: () -> Unit): JComponent {
|
||||
val result = LinkLabel.create(text, action)
|
||||
style?.let { UIUtil.applyStyle(it, result) }
|
||||
return result
|
||||
}
|
||||
|
||||
fun noteComponent(note: String): JComponent {
|
||||
val matcher = HREF_PATTERN.matcher(note)
|
||||
if (!matcher.find()) {
|
||||
return Label(note)
|
||||
}
|
||||
|
||||
val noteComponent = SimpleColoredComponent()
|
||||
var prev = 0
|
||||
do {
|
||||
if (matcher.start() != prev) {
|
||||
noteComponent.append(note.substring(prev, matcher.start()))
|
||||
}
|
||||
noteComponent.append(matcher.group(2), LINK_TEXT_ATTRIBUTES, SimpleColoredComponent.BrowserLauncherTag(matcher.group(1)))
|
||||
prev = matcher.end()
|
||||
}
|
||||
while (matcher.find())
|
||||
|
||||
LinkMouseListenerBase.installSingleTagOn(noteComponent)
|
||||
|
||||
if (prev < note.length) {
|
||||
noteComponent.append(note.substring(prev))
|
||||
}
|
||||
|
||||
return noteComponent
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun htmlComponent(text: String = "", font: Font = UIUtil.getLabelFont(), background: Color? = null, foreground: Color? = null, lineWrap: Boolean = false): JEditorPane {
|
||||
val pane = SwingHelper.createHtmlViewer(lineWrap, font, background, foreground)
|
||||
if (!text.isNullOrEmpty()) {
|
||||
pane.text = "<html><head>${UIUtil.getCssFontDeclaration(font, UIUtil.getLabelForeground(), null, null)}</head><body>$text</body></html>"
|
||||
}
|
||||
pane.border = null
|
||||
pane.disabledTextColor = UIUtil.getLabelDisabledForeground()
|
||||
pane.addHyperlinkListener(BrowserHyperlinkListener.INSTANCE)
|
||||
return pane
|
||||
}
|
||||
|
||||
fun RadioButton(text: String) = JRadioButton(BundleBase.replaceMnemonicAmpersand(text))
|
||||
|
||||
fun CheckBox(text: String, selected: Boolean = false, toolTip: String? = null): JCheckBox {
|
||||
val component = JCheckBox(BundleBase.replaceMnemonicAmpersand(text), selected)
|
||||
toolTip?.let { component.toolTipText = it }
|
||||
return component
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun Panel(title: String? = null, layout: LayoutManager2 = BorderLayout()): JPanel {
|
||||
val panel = JPanel(layout)
|
||||
title?.let { setTitledBorder(it, panel) }
|
||||
return panel
|
||||
}
|
||||
|
||||
fun dialog(title: String,
|
||||
panel: JComponent,
|
||||
resizable: Boolean = false,
|
||||
focusedComponent: JComponent? = null,
|
||||
okActionEnabled: Boolean = true,
|
||||
project: Project? = null,
|
||||
parent: Component? = null,
|
||||
errorText: String? = null,
|
||||
ok: (() -> Unit)? = null): DialogWrapper {
|
||||
return object: DialogWrapper(project, parent, true, IdeModalityType.PROJECT) {
|
||||
// var okActionEnabled: Boolean
|
||||
// get() = okAction.isEnabled
|
||||
// set(value) {
|
||||
// okAction.isEnabled = value
|
||||
// }
|
||||
|
||||
init {
|
||||
setTitle(title)
|
||||
setResizable(resizable)
|
||||
|
||||
if (!okActionEnabled) {
|
||||
this.okAction.isEnabled = false
|
||||
}
|
||||
|
||||
setErrorText(errorText)
|
||||
|
||||
init()
|
||||
}
|
||||
|
||||
override fun createCenterPanel() = panel
|
||||
|
||||
override fun getPreferredFocusedComponent() = focusedComponent
|
||||
|
||||
override fun doOKAction() {
|
||||
ok?.let {
|
||||
if (okAction.isEnabled) {
|
||||
it()
|
||||
}
|
||||
}
|
||||
super.doOKAction()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.ui.layout
|
||||
|
||||
import com.intellij.ui.components.Label
|
||||
import java.awt.Container
|
||||
import javax.swing.ButtonGroup
|
||||
import javax.swing.JLabel
|
||||
|
||||
class LayoutBuilder(val `$`: LayoutBuilderImpl, val buttonGroup: ButtonGroup? = null) {
|
||||
inline fun row(label: String, init: Row.() -> Unit) {
|
||||
row(Label(label), init)
|
||||
}
|
||||
|
||||
inline fun row(label: JLabel? = null, init: Row.() -> Unit) {
|
||||
val row = Row(buttonGroup, label != null)
|
||||
label?.let { row.apply { label() } }
|
||||
row.init()
|
||||
`$`.addRow(row)
|
||||
}
|
||||
|
||||
fun noteRow(text: String) {
|
||||
`$`.noteRow(text)
|
||||
}
|
||||
|
||||
inline fun buttonGroup(init: LayoutBuilder.() -> Unit) {
|
||||
LayoutBuilder(`$`, buttonGroup).init()
|
||||
}
|
||||
}
|
||||
|
||||
interface LayoutBuilderImpl {
|
||||
fun addRow(row: Row)
|
||||
|
||||
fun build(container: Container)
|
||||
|
||||
fun noteRow(text: String)
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.ui.layout
|
||||
|
||||
import com.intellij.ui.components.noteComponent
|
||||
import com.intellij.util.SmartList
|
||||
import net.miginfocom.layout.BoundSize
|
||||
import net.miginfocom.layout.CC
|
||||
import java.awt.Container
|
||||
import javax.swing.JComponent
|
||||
import javax.swing.text.JTextComponent
|
||||
|
||||
/**
|
||||
* Automatically add `grow` to text components (see isAddGrowX).
|
||||
*/
|
||||
internal class MigLayoutBuilder : LayoutBuilderImpl {
|
||||
private val rows = SmartList<Row>()
|
||||
|
||||
override fun addRow(row: Row) {
|
||||
rows.add(row)
|
||||
}
|
||||
|
||||
override fun noteRow(text: String) {
|
||||
// add empty row
|
||||
addRow(Row(null))
|
||||
|
||||
val row = Row(null, spanned = true)
|
||||
row.apply { (noteComponent(text))() }
|
||||
addRow(row)
|
||||
}
|
||||
|
||||
override fun build(container: Container) {
|
||||
val labeled = rows.firstOrNull(Row::labeled) != null
|
||||
var gapTop = -1
|
||||
for (row in rows) {
|
||||
val lastComponent = row.components.lastOrNull()
|
||||
if (lastComponent == null) {
|
||||
if (row === rows.first()) {
|
||||
// do not add gap for the first row
|
||||
continue
|
||||
}
|
||||
|
||||
// https://docs.google.com/document/d/1DKnLkO-7_onA7_NCw669aeMH5ltNvw-QMiQHnXu8k_Y/edit#heading=h.c3849zu3vjhq
|
||||
// gap = 10u where u = 4px
|
||||
gapTop = VERTICAL_GAP * 3
|
||||
}
|
||||
|
||||
for ((index, component) in row.components.withIndex()) {
|
||||
// MigLayout in any case always creates CC, so, create instance even if it is not required
|
||||
val cc = CC()
|
||||
|
||||
if (gapTop != -1) {
|
||||
cc.vertical.gapBefore = gapToBoundSize(gapTop, false)
|
||||
gapTop = -1
|
||||
}
|
||||
|
||||
if (isAddGrowX(component)) {
|
||||
cc.growX()
|
||||
}
|
||||
|
||||
if (component === lastComponent) {
|
||||
cc.wrap()
|
||||
// Set span for last component because cell count in other rows may be greater — but we expect that last component
|
||||
// Spanned row always contains only one component, so, handled as general case.
|
||||
cc.spanX()
|
||||
}
|
||||
|
||||
if (labeled && !row.spanned && !row.labeled && component === row.components.first()) {
|
||||
cc.skip()
|
||||
}
|
||||
|
||||
if (index >= row.rightIndex) {
|
||||
cc.horizontal.gapBefore = BoundSize(null, null, null, true, null)
|
||||
}
|
||||
|
||||
container.add(component, cc)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isAddGrowX(component: JComponent) = component is JTextComponent
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.ui.layout
|
||||
|
||||
import com.intellij.BundleBase
|
||||
import com.intellij.ui.components.Label
|
||||
import com.intellij.ui.components.Link
|
||||
import com.intellij.util.SmartList
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import java.awt.event.ActionEvent
|
||||
import javax.swing.ButtonGroup
|
||||
import javax.swing.JButton
|
||||
import javax.swing.JComponent
|
||||
import javax.swing.JLabel
|
||||
|
||||
class Row(private val buttonGroup: ButtonGroup?, internal val labeled: Boolean = false, internal val spanned: Boolean = false) {
|
||||
var rightIndex = Int.MAX_VALUE
|
||||
val components = SmartList<JComponent>()
|
||||
|
||||
fun label(text: String, style: UIUtil.ComponentStyle? = null, fontColor: UIUtil.FontColor? = null, bold: Boolean = false) {
|
||||
Label(text, style, fontColor, bold)()
|
||||
}
|
||||
|
||||
fun link(text: String, style: UIUtil.ComponentStyle? = null, action: () -> Unit) {
|
||||
val result = Link(text, action = action)
|
||||
style?.let { UIUtil.applyStyle(it, result) }
|
||||
result()
|
||||
}
|
||||
|
||||
fun button(text: String, actionListener: (event: ActionEvent) -> Unit) {
|
||||
val button = JButton(BundleBase.replaceMnemonicAmpersand(text))
|
||||
button.addActionListener(actionListener)
|
||||
button()
|
||||
}
|
||||
|
||||
operator fun JComponent.invoke() {
|
||||
if (buttonGroup != null && this is JButton) {
|
||||
buttonGroup.add(this)
|
||||
}
|
||||
this@Row.components.add(this)
|
||||
}
|
||||
|
||||
inline fun right(init: Row.() -> Unit) {
|
||||
if (rightIndex != Int.MAX_VALUE) {
|
||||
throw IllegalStateException("right allowed only once")
|
||||
}
|
||||
rightIndex = components.size
|
||||
|
||||
init()
|
||||
}
|
||||
|
||||
@Deprecated(message = "Nested row is prohibited", level = DeprecationLevel.ERROR)
|
||||
fun row(label: String, init: Row.() -> Unit) {
|
||||
}
|
||||
|
||||
@Deprecated(message = "Nested row is prohibited", level = DeprecationLevel.ERROR)
|
||||
fun row(label: JLabel? = null, init: Row.() -> Unit) {
|
||||
}
|
||||
|
||||
@Deprecated(message = "Nested noteRow is prohibited", level = DeprecationLevel.ERROR)
|
||||
fun noteRow(text: String) {
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,20 @@ enum class CCFlags {
|
||||
*/
|
||||
span, spanX, spanY,
|
||||
|
||||
/**
|
||||
* Splits the cell in a number of sub cells. Basically this means that the next count number of components will be put in the same cell, next to each other with default gaps.
|
||||
* Only the first component in a cell can set the split, any subsequent "split" keywords in the cell will be ignored.
|
||||
* "count" defaults to infinite if not specified, which means that "split" alone will put all subsequent components in the same cell.
|
||||
* "skip", "wrap" and "newline" will break out of the split cell. The latter two will move to a new row/column as usual.
|
||||
* "skip" will skip out if the splitting and continue in the next cell.
|
||||
*/
|
||||
split,
|
||||
|
||||
grow, push, pushY, pushX, right, skip
|
||||
grow, push, pushY, pushX, right,
|
||||
|
||||
/**
|
||||
* Skips a number of cells in the flow. This is used to jump over a number of cells before the next free cell is looked for.
|
||||
* The skipping is done before this component is put in a cell and thus this cells is affected by it. "count" defaults to 1 if not specified.
|
||||
*/
|
||||
skip
|
||||
}
|
||||
@@ -15,104 +15,69 @@
|
||||
*/
|
||||
package com.intellij.ui.layout
|
||||
|
||||
import com.intellij.BundleBase
|
||||
import com.intellij.ide.BrowserUtil
|
||||
import com.intellij.openapi.ui.ex.MultiLineLabel
|
||||
import com.intellij.ui.components.JBLabel
|
||||
import com.intellij.ui.components.Label
|
||||
import com.intellij.ui.components.RadioButton
|
||||
import com.intellij.ui.components.labels.LinkLabel
|
||||
import com.intellij.ui.noteComponent
|
||||
import com.intellij.ui.components.noteComponent
|
||||
import com.intellij.ui.layout.LCFlags.*
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import com.intellij.util.ui.UIUtil.ComponentStyle
|
||||
import com.intellij.util.ui.UIUtil.FontColor
|
||||
import net.miginfocom.layout.BoundSize
|
||||
import net.miginfocom.layout.CC
|
||||
import net.miginfocom.layout.UnitValue
|
||||
import java.awt.Component
|
||||
import java.awt.Font
|
||||
import java.awt.event.ActionEvent
|
||||
import javax.swing.*
|
||||
|
||||
inline fun panel(vararg layoutConstraints: LCFlags, title: String? = null, init: Panel.() -> Unit) = createPanel(title, layoutConstraints, init)
|
||||
inline fun panel(title: String? = null, init: LayoutBuilder.() -> Unit) = createPanel2(title, init)
|
||||
|
||||
fun JPanel.label(text: String,
|
||||
inline fun verticalPanel(init: Panel.() -> Unit) = createUnsafePanel(layoutConstraints = arrayOf(noGrid, flowY, fillX), init = init)
|
||||
|
||||
fun Panel.label(text: String,
|
||||
vararg constraints: CCFlags,
|
||||
style: ComponentStyle? = null,
|
||||
fontColor: UIUtil.FontColor? = null,
|
||||
fontColor: FontColor? = null,
|
||||
bold: Boolean = false,
|
||||
gapLeft: Int = 0,
|
||||
gapBottom: Int = 0,
|
||||
gapAfter: Int = 0,
|
||||
split: Int = -1) {
|
||||
val finalText = BundleBase.replaceMnemonicAmpersand(text)
|
||||
val label: JLabel
|
||||
if (fontColor == null) {
|
||||
label = if (finalText.contains('\n')) MultiLineLabel(finalText) else JLabel(finalText)
|
||||
style?.let { UIUtil.applyStyle(it, label) }
|
||||
}
|
||||
else {
|
||||
label = JBLabel(finalText, style ?: ComponentStyle.REGULAR, fontColor)
|
||||
}
|
||||
|
||||
if (bold) {
|
||||
label.font = label.font.deriveFont(Font.BOLD)
|
||||
}
|
||||
|
||||
add(label, createComponentConstraints(constraints, gapLeft = gapLeft, gapBottom = gapBottom, gapAfter = gapAfter, split = split))
|
||||
add(Label(text, style, fontColor, bold), createComponentConstraints(constraints, gapLeft = gapLeft, gapBottom = gapBottom, gapAfter = gapAfter, split = split))
|
||||
}
|
||||
|
||||
fun JPanel.link(text: String, vararg constraints: CCFlags, style: ComponentStyle? = null, action: () -> Unit) {
|
||||
fun Panel.link(text: String, vararg constraints: CCFlags, style: ComponentStyle? = null, action: () -> Unit) {
|
||||
val result = LinkLabel.create(text, action)
|
||||
style?.let { UIUtil.applyStyle(it, result) }
|
||||
add(result, constraints.create())
|
||||
}
|
||||
|
||||
fun JPanel.link(text: String, url: String, vararg constraints: CCFlags, style: ComponentStyle? = null) {
|
||||
fun Panel.link(text: String, url: String, vararg constraints: CCFlags, style: ComponentStyle? = null) {
|
||||
val result = LinkLabel.create(text, { BrowserUtil.browse(url) })
|
||||
style?.let { UIUtil.applyStyle(it, result) }
|
||||
add(result, constraints.create())
|
||||
}
|
||||
|
||||
fun JPanel.hint(text: String, vararg constraints: CCFlags) {
|
||||
label(text, style = ComponentStyle.SMALL, fontColor = UIUtil.FontColor.BRIGHTER, constraints = *constraints, gapLeft = 3 * GAP)
|
||||
fun Panel.hint(text: String, vararg constraints: CCFlags) {
|
||||
label(text, style = ComponentStyle.SMALL, fontColor = FontColor.BRIGHTER, constraints = *constraints, gapLeft = 3 * GAP)
|
||||
}
|
||||
|
||||
/**
|
||||
* Hyperlinks are supported (`<a href=""></a>`), new lines and <br> are not supported.
|
||||
*/
|
||||
fun JPanel.note(text: String, vararg constraints: CCFlags) {
|
||||
fun Panel.note(text: String, vararg constraints: CCFlags) {
|
||||
add(noteComponent(text), createComponentConstraints(constraints, gapTop = GAP))
|
||||
}
|
||||
|
||||
fun JPanel.radioButton(text: String, vararg constraints: CCFlags) {
|
||||
fun Panel.radioButton(text: String, vararg constraints: CCFlags) {
|
||||
add(RadioButton(text), constraints.create())
|
||||
}
|
||||
|
||||
fun JPanel.buttonGroup(vararg buttons: AbstractButton) {
|
||||
val group = ButtonGroup()
|
||||
buttons.forEach {
|
||||
group.add(it)
|
||||
add(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun JPanel.button(text: String, vararg constraints: CCFlags, actionListener: (event: ActionEvent) -> Unit) {
|
||||
val button = JButton(BundleBase.replaceMnemonicAmpersand(text))
|
||||
button.addActionListener(actionListener)
|
||||
add(button, constraints.create())
|
||||
}
|
||||
|
||||
inline fun Panel.panel(title: String, layoutConstraints: Array<out LCFlags> = emptyArray(), vararg constraints: CCFlags, init: Panel.() -> Unit) {
|
||||
add(createPanel(title, layoutConstraints, init), constraints.create())
|
||||
}
|
||||
|
||||
fun JPanel.panel(title: String, wrappedComponent: Component, vararg constraints: CCFlags) {
|
||||
val panel = TitledPanel(title)
|
||||
fun Panel.panel(title: String, wrappedComponent: Component, vararg constraints: CCFlags) {
|
||||
val panel = com.intellij.ui.components.Panel(title)
|
||||
panel.add(wrappedComponent)
|
||||
add(panel, constraints.create())
|
||||
}
|
||||
|
||||
fun RadioButton(text: String) = JRadioButton(BundleBase.replaceMnemonicAmpersand(text))
|
||||
|
||||
private fun gapToBoundSize(value: Int, isHorizontal: Boolean): BoundSize {
|
||||
internal fun gapToBoundSize(value: Int, isHorizontal: Boolean): BoundSize {
|
||||
val unitValue = UnitValue(value.toFloat(), "", isHorizontal, UnitValue.STATIC, null)
|
||||
return BoundSize(unitValue, unitValue, null, false, null)
|
||||
}
|
||||
|
||||
@@ -16,11 +16,11 @@
|
||||
package com.intellij.ui.layout
|
||||
|
||||
import com.intellij.ui.IdeBorderFactory
|
||||
import net.miginfocom.layout.BoundSize
|
||||
import net.miginfocom.layout.CC
|
||||
import net.miginfocom.layout.ConstraintParser
|
||||
import net.miginfocom.layout.LC
|
||||
import net.miginfocom.swing.MigLayout
|
||||
import java.awt.BorderLayout
|
||||
import java.awt.Component
|
||||
import java.awt.LayoutManager
|
||||
import javax.swing.JPanel
|
||||
@@ -29,10 +29,11 @@ import javax.swing.JPanel
|
||||
// https://docs.google.com/document/d/1DKnLkO-7_onA7_NCw669aeMH5ltNvw-QMiQHnXu8k_Y/edit
|
||||
|
||||
internal const val GAP = 10
|
||||
internal const val VERTICAL_GAP = 5
|
||||
|
||||
// default values differs to MigLayout - IntelliJ Platform defaults are used
|
||||
// see com.intellij.uiDesigner.core.AbstractLayout.DEFAULT_HGAP and DEFAULT_VGAP (multiplied by 2 to achieve the same look (it seems in terms of MigLayout gap is both left and right space))
|
||||
fun c(insets: String? = "0", gap: String? = "20 5"): LC {
|
||||
fun c(insets: String? = "0", gap: String? = "20 $VERTICAL_GAP"): LC {
|
||||
// no setter for gap, so, create string to parse
|
||||
val lc = if (gap == null) LC() else ConstraintParser.parseLayoutConstraint("gap ${gap}")
|
||||
insets?.let {
|
||||
@@ -42,8 +43,13 @@ fun c(insets: String? = "0", gap: String? = "20 5"): LC {
|
||||
}
|
||||
|
||||
// do not use directly
|
||||
inline fun createPanel(title: String?, layoutConstraints: Array<out LCFlags>, init: Panel.() -> Unit): JPanel {
|
||||
val panel = Panel(MigLayout(c().apply(layoutConstraints)))
|
||||
inline fun createUnsafePanel(title: String? = null, wrap: Int = 0, layoutConstraints: Array<out LCFlags>, init: Panel.() -> Unit): JPanel {
|
||||
val constraints = c().apply(layoutConstraints)
|
||||
if (wrap != 0) {
|
||||
constraints.wrapAfter(wrap)
|
||||
}
|
||||
|
||||
val panel = Panel(MigLayout(constraints))
|
||||
if (title != null) {
|
||||
setTitledBorder(title, panel)
|
||||
}
|
||||
@@ -52,6 +58,17 @@ inline fun createPanel(title: String?, layoutConstraints: Array<out LCFlags>, in
|
||||
return panel
|
||||
}
|
||||
|
||||
inline fun createPanel2(title: String?, init: LayoutBuilder.() -> Unit): JPanel {
|
||||
val builder = createLayoutBuilder()
|
||||
builder.init()
|
||||
|
||||
val panel = com.intellij.ui.components.Panel(title, MigLayout(c().fillX()))
|
||||
builder.`$`.build(panel)
|
||||
return panel
|
||||
}
|
||||
|
||||
fun createLayoutBuilder() = LayoutBuilder(MigLayoutBuilder())
|
||||
|
||||
fun setTitledBorder(title: String, panel: JPanel) {
|
||||
val border = IdeBorderFactory.createTitledBorder(title, false)
|
||||
panel.border = border
|
||||
@@ -61,6 +78,8 @@ fun setTitledBorder(title: String, panel: JPanel) {
|
||||
// we have to use own class because we want to use method `add`, but Kotlin cannot select proper method implementation (not Kotlin bug, but intentional change)
|
||||
// and it is required to add invoke operator fun to Component, but use JPanel as receiver
|
||||
class Panel(layout: LayoutManager) : JPanel(layout) {
|
||||
internal var beforeAdd: ((Component, CC?) -> CC?)? = null
|
||||
|
||||
operator fun Component.invoke(vararg constraints: CCFlags) {
|
||||
add(this, constraints.create())
|
||||
}
|
||||
@@ -72,6 +91,10 @@ class Panel(layout: LayoutManager) : JPanel(layout) {
|
||||
fun add(component: Component, vararg constraints: CCFlags) {
|
||||
add(component, constraints.create())
|
||||
}
|
||||
|
||||
override fun addImpl(component: Component, constraints: Any?, index: Int) {
|
||||
super.addImpl(component, beforeAdd?.invoke(component, constraints as? CC) ?: constraints, index)
|
||||
}
|
||||
}
|
||||
|
||||
fun Array<out CCFlags>.create() = if (isEmpty()) null else CC().apply(this)
|
||||
@@ -81,7 +104,12 @@ fun CC.apply(flags: Array<out CCFlags>): CC {
|
||||
when (flag) {
|
||||
CCFlags.wrap -> isWrap = true
|
||||
CCFlags.grow -> grow()
|
||||
CCFlags.right -> alignX("right")
|
||||
|
||||
// If you have more than one component in a cell the alignment keywords will not work since the behavior would be indeterministic.
|
||||
// You can however accomplish the same thing by setting a gap before and/or after the components.
|
||||
// That gap may have a minimum size of 0 and a preferred size of a really large value to create a "pushing" gap.
|
||||
// There is even a keyword for this: "push". So "gapleft push" will be the same as "align right" and work for multi-component cells as well.
|
||||
CCFlags.right -> horizontal.gapBefore = BoundSize(null, null, null, true, null)
|
||||
|
||||
CCFlags.push -> push()
|
||||
CCFlags.pushX -> pushX()
|
||||
@@ -120,10 +148,4 @@ fun LC.apply(flags: Array<out LCFlags>): LC {
|
||||
|
||||
fun JPanel.add(component: Component, vararg constraints: CCFlags) {
|
||||
add(component, constraints.create())
|
||||
}
|
||||
|
||||
fun TitledPanel(title: String): JPanel {
|
||||
val panel = JPanel(BorderLayout())
|
||||
setTitledBorder(title, panel)
|
||||
return panel
|
||||
}
|
||||
@@ -1,12 +1,18 @@
|
||||
Use `panel` to create UI:
|
||||
|
||||
```kotlin
|
||||
panel(fillX) {
|
||||
// child components
|
||||
panel {
|
||||
row {
|
||||
// child components
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Rows are created vertically from top to bottom, in the same order as lines of code that call `row`.
|
||||
Inside one row, you add components from left to right in the same order calls to factory method or `()` appear in each row.
|
||||
|
||||
There are two ways to add child components:
|
||||
* Using factory methods `label`, `button`, `radioButton`, `hint`, `link`, `note`, `panel`, etc. It allows you to create consistent UI and reuse common patterns (for example, `note` automatically adds required top gap).
|
||||
* Using factory methods `label`, `button`, `radioButton`, `hint`, `link`, etc. It allows you to create consistent UI and reuse common patterns.
|
||||
```kotlin
|
||||
note("""Do not have an account? <a href="https://account.jetbrains.com/login">Sign Up</a>""", span, wrap)
|
||||
```
|
||||
@@ -14,7 +20,7 @@ There are two ways to add child components:
|
||||
```kotlin
|
||||
val userField = JTextField(credentials?.userName)
|
||||
panel() {
|
||||
userField(grow, wrap)
|
||||
row { userField(grow, wrap) }
|
||||
}
|
||||
// use userField variable somehow
|
||||
```
|
||||
@@ -23,21 +29,19 @@ There are two ways to add child components:
|
||||
```kotlin
|
||||
JTextField(credentials?.userName)(grow, wrap)
|
||||
```
|
||||
|
||||
|
||||
Example:
|
||||
```kotlin
|
||||
val panel = panel(fillX) {
|
||||
label("Login to get notified when the submitted\nexceptions are fixed.", span, wrap)
|
||||
label("Username:")
|
||||
userField(grow, wrap)
|
||||
|
||||
label("Password:")
|
||||
passwordField(grow, wrap)
|
||||
rememberCheckBox(skip, split, grow)
|
||||
link("Forgot password?", wrap, right) {
|
||||
// custom action
|
||||
val panel = panel {
|
||||
noteRow("Login to get notified when the submitted\nexceptions are fixed.")
|
||||
row("Username:") { userField() }
|
||||
row("Password:") { passwordField() }
|
||||
row {
|
||||
rememberCheckBox()
|
||||
right {
|
||||
link("Forgot password?") { /* custom action */ }
|
||||
}
|
||||
}
|
||||
|
||||
note("""Do not have an account? <a href="https://example.com/login">Sign Up</a>""", span, wrap)
|
||||
noteRow("""Do not have an account? <a href="https://account.jetbrains.com/login">Sign Up</a>""")
|
||||
}
|
||||
```
|
||||
+2
-2
@@ -24,7 +24,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import static com.intellij.ui.layout.LayoutImplKt.TitledPanel;
|
||||
import static com.intellij.ui.components.ComponentsKt.Panel;
|
||||
|
||||
public class DataViewsConfigurableUi {
|
||||
public static final String DEBUGGER_VALUE_TOOLTIP_AUTO_SHOW_KEY = "debugger.valueTooltipAutoShow";
|
||||
@@ -91,6 +91,6 @@ public class DataViewsConfigurableUi {
|
||||
}
|
||||
|
||||
private void createUIComponents() {
|
||||
myEditorSettingsPanel = TitledPanel("Editor");
|
||||
myEditorSettingsPanel = Panel("Editor");
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import com.intellij.openapi.application.ModalityState;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.util.PathUtil;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import git4idea.config.SSHConnectionSettings;
|
||||
@@ -35,6 +36,8 @@ import java.util.Vector;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static com.intellij.credentialStore.CredentialAttributesKt.CredentialAttributes;
|
||||
|
||||
/**
|
||||
* Swing GUI handler for the SSH events
|
||||
*/
|
||||
@@ -69,10 +72,12 @@ public class GitSSHGUIHandler {
|
||||
@Nullable
|
||||
public String askPassphrase(final String username, final String keyPath, boolean resetPassword, final String lastError) {
|
||||
String error = processLastError(resetPassword, lastError);
|
||||
return PasswordSafePromptDialog.askPassphrase(myProject, GitBundle.getString("ssh.ask.passphrase.title"),
|
||||
GitBundle.message("ssh.askPassphrase.message", keyPath, username),
|
||||
GitSSHGUIHandler.class, "PASSPHRASE:" + keyPath, resetPassword, error
|
||||
);
|
||||
return PasswordSafePromptDialog.askPassword(myProject,
|
||||
GitBundle.getString("ssh.ask.passphrase.title"),
|
||||
"Password for the SSH key \"" + PathUtil.getFileName(keyPath) + "\":",
|
||||
CredentialAttributes(GitSSHGUIHandler.class, "PASSPHRASE:" + keyPath),
|
||||
resetPassword,
|
||||
error);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -349,8 +349,6 @@ root.tracker.message.title=Invalid Git Roots
|
||||
root.tracker.message=<p>Some configured Git VCS roots are not under Git or have Git repositories in subdirectories without a configured VCS root. <a href="">Configure.</a></p>
|
||||
select.branch.to.checkout=Select branch to checkout
|
||||
show.all.paths.affected.action.name=Show All Affected Paths
|
||||
ssh.ask.passphrase.title=SSH Key Passphrase
|
||||
ssh.askPassphrase.message=Please enter passphrase for the private key {0} (the user name is {1})
|
||||
ssh.changed.host.key=<html>The server host key for the host {0}:{1} has changed to {2} (type {3}).\nDo you want to accept the changed key?</html>
|
||||
ssh.confirm.key.titile=Confirm SSH Server Key
|
||||
ssh.error.title=Authentication error
|
||||
|
||||
@@ -38,5 +38,4 @@ login.other.git.provider.note=Consider to configure <a href="https://help.github
|
||||
|
||||
settings.upstream.url=Upstream URL\:
|
||||
|
||||
log.in.to=Log in to {0}.
|
||||
enter.your.password.for.ssh.key=Enter your password for the SSH key \"{0}\".
|
||||
log.in.to=Log in to {0}.
|
||||
@@ -20,7 +20,6 @@ import com.intellij.openapi.options.ConfigurableBase
|
||||
import com.intellij.openapi.options.ConfigurableUi
|
||||
import com.intellij.ui.layout.*
|
||||
import com.intellij.ui.layout.CCFlags.*
|
||||
import com.intellij.ui.layout.LCFlags.*
|
||||
import javax.swing.JCheckBox
|
||||
|
||||
internal class IcsConfigurable : ConfigurableBase<IcsConfigurableUi, IcsSettings>("ics", icsMessage("ics.settings"), "reference.settings.ics") {
|
||||
@@ -56,7 +55,7 @@ internal class IcsConfigurableUi : ConfigurableUi<IcsSettings>, Disposable {
|
||||
saveSettings(settings, icsManager.settingsFile)
|
||||
}
|
||||
|
||||
override fun getComponent() = panel(noGrid, flowY) {
|
||||
override fun getComponent() = verticalPanel {
|
||||
editors.get(0).component()
|
||||
autoSync()
|
||||
hint("Use VCS -> Sync Settings to sync when you want")
|
||||
|
||||
@@ -19,11 +19,9 @@ import com.intellij.credentialStore.Credentials
|
||||
import com.intellij.credentialStore.OneTimeString
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.invokeAndWaitIfNeed
|
||||
import com.intellij.openapi.ui.dialog
|
||||
import com.intellij.ui.DocumentAdapter
|
||||
import com.intellij.ui.components.dialog
|
||||
import com.intellij.ui.layout.*
|
||||
import com.intellij.ui.layout.CCFlags.*
|
||||
import com.intellij.ui.layout.LCFlags.*
|
||||
import com.intellij.util.PathUtilRt
|
||||
import com.intellij.util.text.nullize
|
||||
import com.intellij.util.text.trimMiddle
|
||||
@@ -45,43 +43,40 @@ fun showAuthenticationForm(credentials: Credentials?, uri: String, host: String?
|
||||
username = path.substring(1, if (firstSlashIndex == -1) path.length else firstSlashIndex)
|
||||
}
|
||||
|
||||
val message = if (sshKeyFile == null) icsMessage("log.in.to", uri.trimMiddle(50)) else icsMessage("enter.your.password.for.ssh.key", PathUtilRt.getFileName(sshKeyFile))
|
||||
val message = if (sshKeyFile == null) icsMessage("log.in.to", uri.trimMiddle(50)) else "Enter your password for the SSH key \"${PathUtilRt.getFileName(sshKeyFile)}\":"
|
||||
|
||||
return invokeAndWaitIfNeed {
|
||||
val userField = JTextField(username)
|
||||
val passwordField = JPasswordField(credentials?.password?.toString())
|
||||
|
||||
val centerPanel = panel(fillX) {
|
||||
label(message, wrap, span, bold = true, gapBottom = 10)
|
||||
|
||||
val centerPanel = panel {
|
||||
noteRow(message)
|
||||
if (sshKeyFile == null && !isGitHub) {
|
||||
label("Username:")
|
||||
userField(grow, wrap)
|
||||
row("Username:") { userField() }
|
||||
}
|
||||
|
||||
label(if (sshKeyFile == null && isGitHub) "Token:" else "Password:")
|
||||
passwordField(grow, wrap)
|
||||
row(if (sshKeyFile == null && isGitHub) "Token:" else "Password:") { passwordField() }
|
||||
|
||||
note?.let { note(it, skip) }
|
||||
note?.let { noteRow(it) }
|
||||
}
|
||||
|
||||
val authenticationForm = dialog(
|
||||
title = "Settings Repository",
|
||||
centerPanel = centerPanel,
|
||||
preferedFocusComponent = if (userField.parent == null) passwordField else userField,
|
||||
panel = centerPanel,
|
||||
focusedComponent = if (userField.parent == null) passwordField else userField,
|
||||
okActionEnabled = false)
|
||||
|
||||
passwordField.document.addDocumentListener(object : DocumentAdapter() {
|
||||
override fun textChanged(e: DocumentEvent) {
|
||||
authenticationForm.okActionEnabled(e.document.length != 0)
|
||||
authenticationForm.isOKActionEnabled = e.document.length != 0
|
||||
}
|
||||
})
|
||||
authenticationForm.okActionEnabled(false)
|
||||
authenticationForm.isOKActionEnabled = false
|
||||
|
||||
if (authenticationForm.showAndGet()) {
|
||||
username = sshKeyFile ?: userField.text.nullize(true)
|
||||
val passwordChars = passwordField.password
|
||||
Credentials(username, if (passwordChars == null || passwordChars.isEmpty()) (if (username == null) null else OneTimeString("x-oauth-basic")) else OneTimeString(passwordChars))
|
||||
val passwordChars = passwordField.password.nullize()
|
||||
Credentials(username, if (passwordChars == null) (if (username == null) null else OneTimeString("x-oauth-basic")) else OneTimeString(passwordChars))
|
||||
}
|
||||
else {
|
||||
null
|
||||
|
||||
@@ -53,9 +53,10 @@ internal fun createRepositoryListEditor(): ConfigurableUi<IcsSettings> {
|
||||
override fun isModified(settings: IcsSettings) = editor.isModified
|
||||
|
||||
override fun getComponent() = panel {
|
||||
label("Repository:")
|
||||
editor.comboBox()
|
||||
deleteButton()
|
||||
row("Repository:") {
|
||||
editor.comboBox()
|
||||
deleteButton()
|
||||
}
|
||||
}
|
||||
|
||||
override fun apply(settings: IcsSettings) {
|
||||
|
||||
-4
@@ -257,10 +257,6 @@ public class StudyTwitterUtils {
|
||||
init();
|
||||
}
|
||||
|
||||
public void setOKActionEnabled(boolean isEnabled) {
|
||||
super.setOKActionEnabled(isEnabled);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected JComponent createCenterPanel() {
|
||||
|
||||
Reference in New Issue
Block a user