diff --git a/ijPlugin/src/main/kotlin/org/jetbrains/research/tasktracker/config/agreement/Agreement.kt b/ijPlugin/src/main/kotlin/org/jetbrains/research/tasktracker/config/agreement/Agreement.kt index 11a65d6f..17175fec 100644 --- a/ijPlugin/src/main/kotlin/org/jetbrains/research/tasktracker/config/agreement/Agreement.kt +++ b/ijPlugin/src/main/kotlin/org/jetbrains/research/tasktracker/config/agreement/Agreement.kt @@ -7,4 +7,4 @@ import kotlinx.serialization.Serializable */ @Serializable -data class Agreement(val text: String, val required: Boolean = true) +data class Agreement(val text: String, val required: Boolean = true, val openLinkInDefaultBrowser: Boolean = true) diff --git a/ijPlugin/src/main/kotlin/org/jetbrains/research/tasktracker/ui/main/panel/MainPluginPanelFactory.kt b/ijPlugin/src/main/kotlin/org/jetbrains/research/tasktracker/ui/main/panel/MainPluginPanelFactory.kt index 986f1d24..4df90d3d 100644 --- a/ijPlugin/src/main/kotlin/org/jetbrains/research/tasktracker/ui/main/panel/MainPluginPanelFactory.kt +++ b/ijPlugin/src/main/kotlin/org/jetbrains/research/tasktracker/ui/main/panel/MainPluginPanelFactory.kt @@ -26,6 +26,8 @@ import org.jetbrains.research.tasktracker.tracking.toolWindow.ToolWindowTracker import org.jetbrains.research.tasktracker.tracking.webcam.WebCamTracker import org.jetbrains.research.tasktracker.tracking.webcam.collectAllDevices import org.jetbrains.research.tasktracker.ui.main.panel.models.AgreementChecker +import org.jetbrains.research.tasktracker.ui.main.panel.models.ButtonState +import org.jetbrains.research.tasktracker.ui.main.panel.models.LinkType import org.jetbrains.research.tasktracker.ui.main.panel.panelStates.agreementAcceptance import org.jetbrains.research.tasktracker.ui.main.panel.storage.GlobalPluginStorage import org.jetbrains.research.tasktracker.ui.main.panel.template.HtmlTemplate @@ -34,7 +36,6 @@ import org.jetbrains.research.tasktracker.util.UIBundle import java.awt.BorderLayout import java.awt.FlowLayout import java.awt.event.ActionListener -import javax.swing.JButton /** * The class is intended to manage JCEF and Swing components simultaneously, @@ -68,6 +69,7 @@ class MainPluginPanelFactory : ToolWindowFactory { add(buttonPanel, BorderLayout.SOUTH) } toolWindow.component.add(panel) + listenRedirection() } override fun isApplicable(project: Project) = super.isApplicable(project) && JBCefApp.isSupported() @@ -146,25 +148,30 @@ class MainPluginPanelFactory : ToolWindowFactory { } } + /** + * Opening file in editor by html link. + */ fun listenFileRedirection(task: Task) { - mainWindow.executeJavascript( - """ - const files = document.getElementsByClassName('file'); - for (const file of files) { - file.addEventListener('click', function(event) { - event.preventDefault(); - }) - file.onclick = load_file - } - function load_file (){ - file_id = this.getAttribute('data-value'); - - """, - "}", - "file_id" - ) { + mainWindow.jslinkProcess(LinkType.FILE) { focusOnfFileById(task, it) - null + } + } + + /** + * Redirecting to external websites in JCEF. + */ + private fun listenRedirection() { + mainWindow.jslinkProcess(LinkType.JCEF) { + val previousBackState = backButton.getState() + val previousNextState = nextButton.getState() + nextButton.changeState(ButtonState(previousNextState.text, false)) + backButton.changeState( + ButtonState(UIBundle.message("ui.button.back"), true) { + backButton.changeState(previousBackState) + nextButton.changeState(previousNextState) + mainWindow.loadCurrentTemplate() + } + ) } } @@ -185,14 +192,7 @@ class MainPluginPanelFactory : ToolWindowFactory { true } - fun setNextAction(listener: ActionListener) = nextButton.addListener(listener) + fun setNextAction(listener: ActionListener) = nextButton.setListener(listener) - fun setBackAction(listener: ActionListener) = backButton.addListener(listener) - - private fun JButton.addListener(listener: ActionListener) { - actionListeners.forEach { - removeActionListener(it) - } - addActionListener(listener) - } + fun setBackAction(listener: ActionListener) = backButton.setListener(listener) } diff --git a/ijPlugin/src/main/kotlin/org/jetbrains/research/tasktracker/ui/main/panel/MainPluginWindow.kt b/ijPlugin/src/main/kotlin/org/jetbrains/research/tasktracker/ui/main/panel/MainPluginWindow.kt index 97ce29f8..54c40f00 100644 --- a/ijPlugin/src/main/kotlin/org/jetbrains/research/tasktracker/ui/main/panel/MainPluginWindow.kt +++ b/ijPlugin/src/main/kotlin/org/jetbrains/research/tasktracker/ui/main/panel/MainPluginWindow.kt @@ -1,5 +1,6 @@ package org.jetbrains.research.tasktracker.ui.main.panel +import com.intellij.ide.browsers.BrowserLauncher import com.intellij.ide.ui.LafManagerListener import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.util.Disposer @@ -9,6 +10,7 @@ import org.cef.browser.CefFrame import org.cef.handler.CefLoadHandlerAdapter import org.intellij.lang.annotations.Language import org.jetbrains.concurrency.Promise +import org.jetbrains.research.tasktracker.ui.main.panel.models.LinkType import org.jetbrains.research.tasktracker.ui.main.panel.models.Theme import org.jetbrains.research.tasktracker.ui.main.panel.template.DefaultErrorPage import org.jetbrains.research.tasktracker.ui.main.panel.template.HtmlTemplate @@ -40,6 +42,7 @@ class MainPluginWindow(service: MainWindowService) { } } ) + listenRedirectToDefaultBrowser() Disposer.register(service, windowBrowser) } @@ -86,6 +89,19 @@ class MainPluginWindow(service: MainWindowService) { fun loadHtmlTemplate(template: HtmlTemplate) = windowBrowser.loadHTML(template.htmlContent).also { currentTemplate = template } + fun loadCurrentTemplate() { + loadHtmlTemplate(currentTemplate) + } + + /** + * Redirecting to external websites in default browser. + */ + private fun listenRedirectToDefaultBrowser() { + jslinkProcess(LinkType.DEFAULT_BROWSER) { + BrowserLauncher.instance.open(it) + } + } + companion object { const val JS_QUERY_POOL_SIZE = 100 } diff --git a/ijPlugin/src/main/kotlin/org/jetbrains/research/tasktracker/ui/main/panel/Util.kt b/ijPlugin/src/main/kotlin/org/jetbrains/research/tasktracker/ui/main/panel/Util.kt index 36f59c0f..669cd78a 100644 --- a/ijPlugin/src/main/kotlin/org/jetbrains/research/tasktracker/ui/main/panel/Util.kt +++ b/ijPlugin/src/main/kotlin/org/jetbrains/research/tasktracker/ui/main/panel/Util.kt @@ -2,7 +2,10 @@ package org.jetbrains.research.tasktracker.ui.main.panel import com.intellij.openapi.util.io.FileUtil import org.jetbrains.research.tasktracker.config.MainTaskTrackerConfig +import org.jetbrains.research.tasktracker.ui.main.panel.models.ButtonState +import org.jetbrains.research.tasktracker.ui.main.panel.models.LinkType import org.jetbrains.research.tasktracker.util.UIBundle +import java.awt.event.ActionListener import java.io.File import javax.swing.JButton @@ -15,6 +18,57 @@ fun createJButton( isVisible = isVisibleProp } +fun MainPluginWindow.jslinkProcess(type: LinkType, action: (param: String) -> Unit) { + with(type) { + val preventCode = if (prevent) { + """ + link.addEventListener('click', function(event) { + event.preventDefault(); + }); + """.trimIndent() + } else { + System.lineSeparator() + } + executeJavascript( + """ + const elements$name = document.querySelectorAll('$queryFilter'); + for (const link of elements$name) { + $preventCode + link.onclick = get_link$name + } + + function get_link$name() { + link = this.getAttribute('$getAttribute'); + """, + "}", + "link" + ) { + action(it) + null + } + } +} + +/** + * Creates new JButton state and return previous JButton state. + */ +fun JButton.changeState(buttonState: ButtonState) { + buttonState.actionListener?.let { + setListener(buttonState.actionListener) + } + this.text = buttonState.text + isVisible = buttonState.isVisibleProp +} + +fun JButton.getState(): ButtonState = ButtonState(text, isVisible, actionListeners.firstOrNull()) + +fun JButton.setListener(listener: ActionListener) { + actionListeners.forEach { + removeActionListener(it) + } + addActionListener(listener) +} + /** * Saves selected agreements as a json file. * diff --git a/ijPlugin/src/main/kotlin/org/jetbrains/research/tasktracker/ui/main/panel/models/ButtonState.kt b/ijPlugin/src/main/kotlin/org/jetbrains/research/tasktracker/ui/main/panel/models/ButtonState.kt new file mode 100644 index 00000000..735ba5c3 --- /dev/null +++ b/ijPlugin/src/main/kotlin/org/jetbrains/research/tasktracker/ui/main/panel/models/ButtonState.kt @@ -0,0 +1,9 @@ +package org.jetbrains.research.tasktracker.ui.main.panel.models + +import java.awt.event.ActionListener + +data class ButtonState( + val text: String, + val isVisibleProp: Boolean, + val actionListener: ActionListener? = null +) diff --git a/ijPlugin/src/main/kotlin/org/jetbrains/research/tasktracker/ui/main/panel/models/LinkType.kt b/ijPlugin/src/main/kotlin/org/jetbrains/research/tasktracker/ui/main/panel/models/LinkType.kt new file mode 100644 index 00000000..762a7308 --- /dev/null +++ b/ijPlugin/src/main/kotlin/org/jetbrains/research/tasktracker/ui/main/panel/models/LinkType.kt @@ -0,0 +1,10 @@ +package org.jetbrains.research.tasktracker.ui.main.panel.models + +/** + * Types of redirecting links. + */ +enum class LinkType(val queryFilter: String, val prevent: Boolean, val getAttribute: String) { + DEFAULT_BROWSER(".defaultBrowser", true, "href"), + JCEF("a:not(.defaultBrowser):not(.file)", false, "href"), + FILE(".file", true, "data-value") +} diff --git a/ijPlugin/src/main/kotlin/org/jetbrains/research/tasktracker/ui/main/panel/template/AgreementTemplate.kt b/ijPlugin/src/main/kotlin/org/jetbrains/research/tasktracker/ui/main/panel/template/AgreementTemplate.kt index 9d148817..02975454 100644 --- a/ijPlugin/src/main/kotlin/org/jetbrains/research/tasktracker/ui/main/panel/template/AgreementTemplate.kt +++ b/ijPlugin/src/main/kotlin/org/jetbrains/research/tasktracker/ui/main/panel/template/AgreementTemplate.kt @@ -17,13 +17,24 @@ class AgreementTemplate(private val agreements: List) : HtmlBaseFileT buildString { append("""
""") - append("""
""") + append("""""") } }.joinToString("
") - private fun String.withBr() = replace(System.lineSeparator(), "
") + private fun Agreement.toHtml(): String { + var html = text.replace(System.lineSeparator(), "
") + if (openLinkInDefaultBrowser) { + html = html.replace(HREF_PATTERN) { matchResult -> + val href = matchResult.groupValues[1] + """""" + } + } + return html + } companion object { + val HREF_PATTERN = """""".toRegex() + fun loadCurrentTemplate(): AgreementTemplate { val config = TaskTrackerPlugin.mainConfig.agreementConfig ?: error("agreementConfig has not initialized yet!") diff --git a/ijPlugin/src/main/resources/org/jetbrains/research/tasktracker/config/agreement_default.yaml b/ijPlugin/src/main/resources/org/jetbrains/research/tasktracker/config/agreement_default.yaml index 859ed4f5..b21936e7 100644 --- a/ijPlugin/src/main/resources/org/jetbrains/research/tasktracker/config/agreement_default.yaml +++ b/ijPlugin/src/main/resources/org/jetbrains/research/tasktracker/config/agreement_default.yaml @@ -4,4 +4,6 @@ agreements: example required: false - text: "Example link" + - text: "Example link in JCEF link" + openLinkInDefaultBrowser: false - text: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s," \ No newline at end of file