| /* |
| * Copyright (c) 2010-2020 BSI Business Systems Integration AG. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * BSI Business Systems Integration AG - initial API and implementation |
| */ |
| package org.eclipse.scout.sdk.s2i.nls.editor |
| |
| import com.intellij.openapi.project.Project |
| import com.intellij.openapi.ui.DialogPanel |
| import com.intellij.openapi.ui.DialogWrapper |
| import com.intellij.openapi.ui.MessageType |
| import com.intellij.openapi.ui.ValidationInfo |
| import com.intellij.ui.DocumentAdapter |
| import com.intellij.ui.JBColor |
| import com.intellij.ui.components.* |
| import org.eclipse.scout.sdk.core.s.nls.* |
| import org.eclipse.scout.sdk.core.s.nls.TranslationValidator.* |
| import org.eclipse.scout.sdk.core.util.CoreUtils |
| import org.eclipse.scout.sdk.core.util.Strings |
| import org.eclipse.scout.sdk.s2i.EclipseScoutBundle.Companion.message |
| import org.eclipse.scout.sdk.s2i.ui.IndexedFocusTraversalPolicy |
| import org.eclipse.scout.sdk.s2i.ui.TextFieldWithMaxLen |
| import java.awt.* |
| import java.awt.event.KeyEvent |
| import javax.swing.* |
| import javax.swing.event.DocumentEvent |
| |
| class TranslationNewDialog(val project: Project, val store: ITranslationStore, val stack: TranslationStoreStack, val initialKey: String = "") : DialogWrapper(project, true, IdeModalityType.PROJECT) { |
| |
| private val m_dimensionKey = "scout.nls.newTranslationDialog" |
| private val m_languageTextFields = HashMap<Language, JBTextArea>() |
| private var m_keyTextField: JBTextField? = null |
| private var m_copyToClipboardField: JBCheckBox? = null |
| private var m_errorStatusField: JBLabel? = null |
| private var m_createdTranslation: ITranslationEntry? = null |
| |
| init { |
| title = message("create.new.translation.in.x", store.service().type().elementName()) |
| init() |
| } |
| |
| override fun createCenterPanel(): JComponent { |
| val rootPanel = DialogPanel(GridBagLayout()) // do not use applyCallbacks. the API is different in newer IJ versions |
| rootPanel.preferredSize = Dimension(600, 300) |
| |
| // Key |
| rootPanel.add(JBLabel(NlsTableModel.KEY_COLUMN_HEADER_NAME), GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, |
| GridBagConstraints.NONE, Insets(4, 0, 0, 15), 0, 0)) |
| val keyField = TextFieldWithMaxLen(maxLength = 200) |
| keyField.isFocusable = true |
| keyField.text = initialKey |
| m_keyTextField = keyField |
| rootPanel.add(keyField, GridBagConstraints(1, 0, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, |
| GridBagConstraints.HORIZONTAL, Insets(4, 0, 0, 15), 0, 0)) |
| |
| // Copy to clipboard |
| val copyToClipboardField = JBCheckBox(message("copy.key.to.clipboard"), false) |
| copyToClipboardField.toolTipText = message("copy.key.to.clipboard.desc") |
| copyToClipboardField.isFocusable = true |
| m_copyToClipboardField = copyToClipboardField |
| rootPanel.add(copyToClipboardField, GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_END, |
| GridBagConstraints.NONE, Insets(4, 0, 0, 0), 0, 0)) |
| |
| // Tab pane |
| val tabPane = createTabPane() |
| tabPane.isFocusable = true |
| rootPanel.add(tabPane, GridBagConstraints(0, 1, 3, 1, 1.0, 1.0, GridBagConstraints.PAGE_START, |
| GridBagConstraints.BOTH, Insets(10, 0, 0, 0), 0, 0)) |
| |
| // validation label |
| val statusLabel = JBLabel() |
| statusLabel.border = null |
| statusLabel.preferredSize = Dimension(600, 40) |
| statusLabel.verticalAlignment = JLabel.TOP |
| m_errorStatusField = statusLabel |
| rootPanel.add(statusLabel, GridBagConstraints(0, 2, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, |
| GridBagConstraints.HORIZONTAL, Insets(5, 7, 0, 0), 0, 0)) |
| |
| isOKActionEnabled = false |
| installValidation() |
| |
| defaultLanguageTextField().document.addDocumentListener(KeyAutoGenerator()) |
| |
| rootPanel.isFocusTraversalPolicyProvider = true |
| rootPanel.isFocusCycleRoot = true |
| val focusPolicy = IndexedFocusTraversalPolicy() |
| m_languageTextFields.values.forEach { focusPolicy.addComponent(it) } |
| focusPolicy.addComponent(keyTextField()) |
| focusPolicy.addComponent(copyToClipboardField) |
| focusPolicy.addComponent(tabPane) |
| rootPanel.focusTraversalPolicy = focusPolicy |
| |
| return rootPanel |
| } |
| |
| private fun createTabPane(): JBTabbedPane { |
| val tabPane = JBTabbedPane(SwingConstants.TOP, JTabbedPane.SCROLL_TAB_LAYOUT) |
| store.languages().forEach { |
| val txt = JBTextArea() |
| txt.font = keyTextField().font |
| txt.margin = Insets(5, 7, 5, 5) |
| txt.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, setOf(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0))) |
| txt.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, setOf(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, KeyEvent.SHIFT_DOWN_MASK))) |
| m_languageTextFields[it] = txt |
| |
| val panel = JBScrollPane(txt, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED) |
| panel.border = BorderFactory.createLineBorder(JBColor.border()) |
| |
| tabPane.addTab(it.displayName(), panel) |
| } |
| return tabPane |
| } |
| |
| private fun installValidation() { |
| val triggerValidation = object : DocumentAdapter() { |
| override fun textChanged(e: DocumentEvent) { |
| setErrorStatus(listOfNotNull(validateKeyField(), validateDefaultTextField())) |
| } |
| } |
| keyTextField().document.addDocumentListener(triggerValidation) |
| m_languageTextFields.values.forEach { it.document.addDocumentListener(triggerValidation) } |
| } |
| |
| private fun setErrorStatus(infos: List<ValidationInfo>) { |
| val field = errorStatusField() |
| field.text = "" |
| isOKActionEnabled = infos.all { it.okEnabled } |
| if (infos.isEmpty()) { |
| return |
| } |
| |
| val builder = StringBuilder("<html>") |
| val iterator = infos.iterator() |
| var info = iterator.next() |
| builder.append(validationInfoToHtml(info)) |
| while (iterator.hasNext()) { |
| info = iterator.next() |
| builder.append("<br>").append(validationInfoToHtml(info)) |
| } |
| builder.append("</html>") |
| field.text = builder.toString() |
| } |
| |
| private fun validationInfoToHtml(info: ValidationInfo): String { |
| val color = htmlColorString(if (info.warning) MessageType.WARNING.borderColor else ERROR_FOREGROUND_COLOR) |
| val message = Strings.escapeHtml(info.message) |
| return "<font color=\"${color}\">$message</font>" |
| } |
| |
| fun htmlColorString(color: Color): String? { |
| val red = Integer.toHexString(color.red) |
| val green = Integer.toHexString(color.green) |
| val blue = Integer.toHexString(color.blue) |
| return "#" + |
| (if (red.length == 1) "0$red" else red) + |
| (if (green.length == 1) "0$green" else green) + |
| if (blue.length == 1) "0$blue" else blue |
| } |
| |
| private fun validateKeyField(): ValidationInfo? { |
| val key = keyTextField().text ?: "" |
| return toValidationInfo(validateKey(stack, store, key)) |
| } |
| |
| private fun validateDefaultTextField(): ValidationInfo? { |
| val defaultText = defaultLanguageTextField().text |
| return toValidationInfo(validateDefaultText(defaultText)) |
| } |
| |
| private fun toValidationInfo(errorCode: Int): ValidationInfo? { |
| val defaultLangMissing = ValidationInfo(message("please.provide.text.for.lang.x", Language.LANGUAGE_DEFAULT.displayName())) |
| val invalidKey = ValidationInfo(message("please.specify.translation.key.with.desc")) |
| val info = when (errorCode) { |
| OK -> null |
| DEFAULT_TRANSLATION_MISSING_ERROR -> defaultLangMissing |
| DEFAULT_TRANSLATION_EMPTY_ERROR -> defaultLangMissing |
| KEY_EMPTY_ERROR -> invalidKey |
| KEY_ALREADY_EXISTS_ERROR -> ValidationInfo(message("key.already.exists.in.service")) |
| KEY_OVERRIDES_OTHER_STORE_WARNING -> ValidationInfo(message("key.would.override.desc")) |
| KEY_IS_OVERRIDDEN_BY_OTHER_STORE_WARNING -> ValidationInfo(message("key.would.be.overridden.desc")) |
| KEY_OVERRIDES_AND_IS_OVERRIDDEN_WARNING -> ValidationInfo(message("key.overrides.and.is.overridden.desc")) |
| else -> invalidKey |
| } |
| if (info == null || isForbidden(errorCode)) { |
| return info |
| } |
| return info.asWarning().withOKEnabled() |
| } |
| |
| fun defaultLanguageTextField() = m_languageTextFields[Language.LANGUAGE_DEFAULT]!! |
| |
| fun keyTextField() = m_keyTextField!! |
| |
| fun errorStatusField() = m_errorStatusField!! |
| |
| fun createdTranslation() = m_createdTranslation |
| |
| override fun doOKAction() { |
| if (!okAction.isEnabled) { |
| return |
| } |
| doOk() |
| close(OK_EXIT_CODE) |
| } |
| |
| private fun doOk() { |
| val key = keyTextField().text |
| val newTranslation = Translation(key) |
| m_languageTextFields.entries |
| .filter { Strings.hasText(it.value.text) } |
| .forEach { newTranslation.putText(it.key, it.value.text) } |
| m_createdTranslation = stack.addNewTranslation(newTranslation, store) |
| |
| if (m_copyToClipboardField?.isSelected == true) { |
| CoreUtils.setTextToClipboard(key) |
| } |
| } |
| |
| override fun getPreferredFocusedComponent(): JComponent? { |
| return defaultLanguageTextField() |
| } |
| |
| override fun getDimensionServiceKey() = m_dimensionKey |
| |
| private inner class KeyAutoGenerator : DocumentAdapter() { |
| private val m_defaultLangTextField = defaultLanguageTextField() |
| private var m_lastDefaultLangText: String? = null |
| |
| override fun textChanged(e: DocumentEvent) { |
| val defaultLangText = m_defaultLangTextField.text |
| if (Strings.isBlank(defaultLangText)) { |
| return |
| } |
| val oldKey = stack.generateNewKey(m_lastDefaultLangText) |
| val curKey = Strings.notBlank(keyTextField().text).orElse("") |
| if (curKey == oldKey) { |
| keyTextField().text = stack.generateNewKey(defaultLangText) |
| } |
| m_lastDefaultLangText = defaultLangText |
| } |
| } |
| } |