From 3433596217584d957653c2c33a6fc2ac521cbac1 Mon Sep 17 00:00:00 2001
From: Janne Koschinski <janne@kuschku.de>
Date: Tue, 17 Apr 2018 03:51:36 +0200
Subject: [PATCH] Add highlight rule manager

---
 .../quassel/syncables/HighlightRuleManager.kt | 129 ++++++++++++++++++
 .../quassel/syncables/IgnoreListManager.kt    |  18 +--
 .../interfaces/IHighlightRuleManager.kt       |  95 +++++++++++++
 .../interfaces/IIgnoreListManager.kt          |   1 +
 .../syncables/interfaces/invokers/Invokers.kt |   1 +
 .../de/kuschku/libquassel/session/ISession.kt |   2 +
 .../de/kuschku/libquassel/session/Session.kt  |   3 +
 7 files changed, 234 insertions(+), 15 deletions(-)
 create mode 100644 lib/src/main/java/de/kuschku/libquassel/quassel/syncables/HighlightRuleManager.kt
 create mode 100644 lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IHighlightRuleManager.kt

diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/HighlightRuleManager.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/HighlightRuleManager.kt
new file mode 100644
index 000000000..670ea62a6
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/HighlightRuleManager.kt
@@ -0,0 +1,129 @@
+package de.kuschku.libquassel.quassel.syncables
+
+import de.kuschku.libquassel.protocol.*
+import de.kuschku.libquassel.quassel.syncables.interfaces.IHighlightRuleManager
+import de.kuschku.libquassel.session.SignalProxy
+
+class HighlightRuleManager(
+  proxy: SignalProxy
+) : SyncableObject(proxy, "HighlightRuleManager"), IHighlightRuleManager {
+  data class HighlightRule(
+    val name: String,
+    val isRegEx: Boolean = false,
+    val isCaseSensitive: Boolean = false,
+    val isEnabled: Boolean = true,
+    val isInverse: Boolean = false,
+    val sender: String,
+    val channel: String
+  )
+
+  override fun toVariantMap(): QVariantMap = mapOf(
+    "HighlightRuleList" to QVariant.of(initHighlightRuleList(), Type.QVariantMap),
+    "HighlightNick" to QVariant.of(_highlightNick.value, Type.Int),
+    "NicksCaseSensitive" to QVariant.of(_nicksCaseSensitive, Type.Bool)
+  )
+
+  override fun fromVariantMap(properties: QVariantMap) {
+    initSetHighlightRuleList(properties["HighlightRuleList"].valueOr(::emptyMap))
+    _highlightNick = properties["HighlightNick"].value<Int>()?.let {
+      IHighlightRuleManager.HighlightNickType.of(it)
+    } ?: _highlightNick
+    _nicksCaseSensitive = properties["NicksCaseSensitive"].value(_nicksCaseSensitive)
+  }
+
+  override fun initHighlightRuleList(): QVariantMap = mapOf(
+    "name" to QVariant.of(_highlightRuleList.map {
+      QVariant.of(it.name, Type.QString)
+    }, Type.QVariantList),
+    "isRegEx" to QVariant.of(_highlightRuleList.map {
+      QVariant.of(it.isRegEx, Type.Bool)
+    }, Type.QStringList),
+    "isCaseSensitive" to QVariant.of(_highlightRuleList.map {
+      QVariant.of(it.isCaseSensitive, Type.Bool)
+    }, Type.QVariantList),
+    "isEnabled" to QVariant.of(_highlightRuleList.map {
+      QVariant.of(it.isEnabled, Type.Bool)
+    }, Type.QVariantList),
+    "isInverse" to QVariant.of(_highlightRuleList.map {
+      QVariant.of(it.isInverse, Type.Bool)
+    }, Type.QVariantList),
+    "sender" to QVariant.of(_highlightRuleList.map {
+      QVariant.of(it.sender, Type.QString)
+    }, Type.QStringList),
+    "channel" to QVariant.of(_highlightRuleList.map {
+      QVariant.of(it.channel, Type.QString)
+    }, Type.QVariantList)
+  )
+
+  override fun initSetHighlightRuleList(highlightRuleList: QVariantMap) {
+    val nameList = highlightRuleList["name"].valueOr<QStringList>(::emptyList)
+    val isRegExList = highlightRuleList["isRegEx"].valueOr<QVariantList>(::emptyList)
+    val isCaseSensitiveList = highlightRuleList["isCaseSensitive"].valueOr<QVariantList>(::emptyList)
+    val isEnabledList = highlightRuleList["isEnabled"].valueOr<QVariantList>(::emptyList)
+    val isInverseList = highlightRuleList["isInverse"].valueOr<QVariantList>(::emptyList)
+    val senderList = highlightRuleList["sender"].valueOr<QStringList>(::emptyList)
+    val channelList = highlightRuleList["channel"].valueOr<QStringList>(::emptyList)
+    val size = nameList.size
+    if (isRegExList.size != size || isCaseSensitiveList.size != size ||
+        isEnabledList.size != size || isInverseList.size != size || senderList.size != size ||
+        channelList.size != size)
+      return
+
+    _highlightRuleList = List(size, {
+      HighlightRule(
+        name = nameList[it] ?: "",
+        isRegEx = isRegExList[it].value(false),
+        isCaseSensitive = isCaseSensitiveList[it].value(false),
+        isEnabled = isEnabledList[it].value(false),
+        isInverse = isInverseList[it].value(false),
+        sender = senderList[it] ?: "",
+        channel = channelList[it] ?: ""
+      )
+    })
+  }
+
+  override fun removeHighlightRule(highlightRule: String) = removeAt(indexOf(highlightRule))
+
+  override fun toggleHighlightRule(highlightRule: String) {
+    _highlightRuleList = _highlightRuleList.map {
+      if (it.name == highlightRule) it.copy(isEnabled = !it.isEnabled) else it
+    }
+  }
+
+  override fun addHighlightRule(name: String, isRegEx: Boolean, isCaseSensitive: Boolean,
+                                isEnabled: Boolean, isInverse: Boolean, sender: String,
+                                chanName: String) {
+    if (contains(name)) return
+
+    _highlightRuleList += HighlightRule(
+      name, isRegEx, isCaseSensitive, isEnabled, isInverse, sender, chanName
+    )
+  }
+
+  override fun setHighlightNick(highlightNick: Int) {
+    _highlightNick = IHighlightRuleManager.HighlightNickType.of(highlightNick) ?: _highlightNick
+  }
+
+  override fun setNicksCaseSensitive(nicksCaseSensitive: Boolean) {
+    _nicksCaseSensitive = nicksCaseSensitive
+  }
+
+  fun indexOf(name: String): Int = _highlightRuleList.indexOfFirst { it.name == name }
+  fun contains(name: String) = _highlightRuleList.any { it.name == name }
+
+  fun isEmpty() = _highlightRuleList.isEmpty()
+  fun count() = _highlightRuleList.count()
+  fun removeAt(index: Int) {
+    _highlightRuleList = _highlightRuleList.drop(index)
+  }
+
+  operator fun get(index: Int) = _highlightRuleList[index]
+  fun highlightRuleList() = _highlightRuleList
+  fun setHighlightRuleList(list: List<HighlightRule>) {
+    _highlightRuleList = list
+  }
+
+  private var _highlightRuleList = emptyList<HighlightRule>()
+  private var _highlightNick = IHighlightRuleManager.HighlightNickType.CurrentNick
+  private var _nicksCaseSensitive = false
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IgnoreListManager.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IgnoreListManager.kt
index 1f4b085a0..d30b9b453 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IgnoreListManager.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IgnoreListManager.kt
@@ -80,12 +80,12 @@ class IgnoreListManager constructor(
 
   override fun toggleIgnoreRule(ignoreRule: String) {
     _ignoreList = _ignoreList.map {
-      if (it.ignoreRule == ignoreRule) it.toggleActive() else it
+      if (it.ignoreRule == ignoreRule) it.copy(isActive = !it.isActive) else it
     }
   }
 
-  fun indexOf(ignore: String): Int = _ignoreList.map(IgnoreListItem::ignoreRule).indexOf(ignore)
-  fun contains(ignore: String) = _ignoreList.map(IgnoreListItem::ignoreRule).contains(ignore)
+  fun indexOf(ignore: String): Int = _ignoreList.indexOfFirst { it.ignoreRule == ignore }
+  fun contains(ignore: String) = _ignoreList.any { it.ignoreRule == ignore }
   fun isEmpty() = _ignoreList.isEmpty()
   fun count() = _ignoreList.count()
   fun removeAt(index: Int) {
@@ -167,18 +167,6 @@ class IgnoreListManager constructor(
         .toSet()
     )
 
-    fun toggleActive(isActive: Boolean = !this.isActive) = IgnoreListItem(
-      type = type,
-      ignoreRule = ignoreRule,
-      isRegEx = isRegEx,
-      strictness = strictness,
-      scope = scope,
-      scopeRule = scopeRule,
-      isActive = isActive,
-      scopeRegEx = scopeRegEx,
-      regEx = regEx
-    )
-
     fun copy(
       type: IgnoreType = this.type,
       ignoreRule: String = this.ignoreRule,
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IHighlightRuleManager.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IHighlightRuleManager.kt
new file mode 100644
index 000000000..dded2caa0
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IHighlightRuleManager.kt
@@ -0,0 +1,95 @@
+package de.kuschku.libquassel.quassel.syncables.interfaces
+
+import de.kuschku.libquassel.annotations.Slot
+import de.kuschku.libquassel.annotations.Syncable
+import de.kuschku.libquassel.protocol.ARG
+import de.kuschku.libquassel.protocol.QVariantMap
+import de.kuschku.libquassel.protocol.Type
+
+@Syncable(name = "HighlightRuleManager")
+interface IHighlightRuleManager : ISyncableObject {
+  enum class HighlightNickType(val value: Int) {
+    NoNick(0x00),
+    CurrentNick(0x01),
+    AllNicks(0x02);
+
+    companion object {
+      private val map = values().associateBy(HighlightNickType::value)
+      fun of(value: Int) = map[value]
+    }
+  }
+
+  fun initHighlightRuleList(): QVariantMap
+  fun initSetHighlightRuleList(highlightRuleList: QVariantMap)
+
+  /**
+   * Request removal of an ignore rule based on the rule itself.
+   * Use this method if you want to remove a single ignore rule
+   * and get that synced with the core immediately.
+   * @param highlightRule A valid ignore rule
+   */
+  @Slot
+  fun requestRemoveHighlightRule(highlightRule: String) {
+    REQUEST("requestRemoveHighlightRule", ARG(highlightRule, Type.QString))
+  }
+
+  @Slot
+  fun removeHighlightRule(highlightRule: String)
+
+  /**
+   * Request toggling of "isEnabled" flag of a given ignore rule.
+   * Use this method if you want to toggle the "isEnabled" flag of a single ignore rule
+   * and get that synced with the core immediately.
+   * @param highlightRule A valid ignore rule
+   */
+  @Slot
+  fun requestToggleHighlightRule(highlightRule: String) {
+    REQUEST("requestToggleHighlightRule", ARG(highlightRule, Type.QString))
+  }
+
+  @Slot
+  fun toggleHighlightRule(highlightRule: String)
+
+  /**
+   * Request an HighlightRule to be added to the ignore list
+   * Items added to the list with this method, get immediately synced with the core
+   * @param name The rule
+   * @param isRegEx If the rule should be interpreted as a nickname, or a regex
+   * @param isCaseSensitive If the rule should be interpreted as case-sensitive
+   * @param isEnabled If the rule is active
+   * @param chanName The channel in which the rule should apply
+   */
+  @Slot
+  fun requestAddHighlightRule(name: String, isRegEx: Boolean, isCaseSensitive: Boolean,
+                              isEnabled: Boolean,
+                              isInverse: Boolean, sender: String, chanName: String) {
+    REQUEST("requestAddHighlightRule", ARG(name, Type.QString), ARG(isRegEx, Type.Bool),
+            ARG(isCaseSensitive, Type.Bool), ARG(isEnabled, Type.Bool), ARG(isInverse, Type.Bool),
+            ARG(sender, Type.QString), ARG(chanName, Type.QString))
+  }
+
+  @Slot
+  fun addHighlightRule(name: String, isRegEx: Boolean, isCaseSensitive: Boolean, isEnabled: Boolean,
+                       isInverse: Boolean, sender: String, chanName: String)
+
+  @Slot
+  fun requestSetHighlightNick(highlightNick: Int) {
+    REQUEST("requestSetHighlightNick", ARG(highlightNick, Type.Int))
+  }
+
+  @Slot
+  fun setHighlightNick(highlightNick: Int)
+
+  @Slot
+  fun requestSetNicksCaseSensitive(nicksCaseSensitive: Boolean) {
+    REQUEST("requestSetNicksCaseSensitive", ARG(nicksCaseSensitive, Type.Bool))
+  }
+
+  @Slot
+  fun setNicksCaseSensitive(nicksCaseSensitive: Boolean)
+
+  @Slot
+  override fun update(properties: QVariantMap) {
+    super.update(properties)
+  }
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IIgnoreListManager.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IIgnoreListManager.kt
index 26dec34a6..48fc00126 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IIgnoreListManager.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IIgnoreListManager.kt
@@ -10,6 +10,7 @@ import de.kuschku.libquassel.protocol.Type
 interface IIgnoreListManager : ISyncableObject {
   fun initIgnoreList(): QVariantMap
   fun initSetIgnoreList(ignoreList: QVariantMap)
+
   @Slot
   fun addIgnoreListItem(type: Int, ignoreRule: String, isRegEx: Boolean, strictness: Int,
                         scope: Int, scopeRule: String, isActive: Boolean)
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/invokers/Invokers.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/invokers/Invokers.kt
index 10c951642..e0c4a4fd2 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/invokers/Invokers.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/invokers/Invokers.kt
@@ -26,6 +26,7 @@ object Invokers {
     register(invoker<IDccConfig>())
     register(invoker<IIdentity>())
     register(invoker<IIgnoreListManager>())
+    register(invoker<IHighlightRuleManager>())
     register(invoker<IIrcChannel>())
     register(invoker<IIrcListHelper>())
     register(invoker<IIrcUser>())
diff --git a/lib/src/main/java/de/kuschku/libquassel/session/ISession.kt b/lib/src/main/java/de/kuschku/libquassel/session/ISession.kt
index 8e1762ebd..6891eb9c5 100644
--- a/lib/src/main/java/de/kuschku/libquassel/session/ISession.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/session/ISession.kt
@@ -27,6 +27,7 @@ interface ISession : Closeable {
   val identities: Map<IdentityId, Identity>
   fun liveIdentities(): Observable<Map<IdentityId, Identity>>
   val ignoreListManager: IgnoreListManager?
+  val highlightRuleManager: HighlightRuleManager?
   val ircListHelper: IrcListHelper?
   val networks: Map<NetworkId, Network>
   fun liveNetworks(): Observable<Map<NetworkId, Network>>
@@ -60,6 +61,7 @@ interface ISession : Closeable {
       override val identities: Map<IdentityId, Identity> = emptyMap()
       override fun liveIdentities() = Observable.empty<Map<IdentityId, Identity>>()
       override val ignoreListManager: IgnoreListManager? = null
+      override val highlightRuleManager: HighlightRuleManager? = null
       override val ircListHelper: IrcListHelper? = null
       override val networks: Map<NetworkId, Network> = emptyMap()
       override fun liveNetworks() = Observable.empty<Map<NetworkId, Network>>()
diff --git a/lib/src/main/java/de/kuschku/libquassel/session/Session.kt b/lib/src/main/java/de/kuschku/libquassel/session/Session.kt
index 293c1f94a..e9c44c094 100644
--- a/lib/src/main/java/de/kuschku/libquassel/session/Session.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/session/Session.kt
@@ -52,6 +52,7 @@ class Session(
   override fun liveIdentities(): Observable<Map<IdentityId, Identity>> = live_identities.map { identities.toMap() }
 
   override val ignoreListManager = IgnoreListManager(this)
+  override val highlightRuleManager = HighlightRuleManager(this)
   override val ircListHelper = IrcListHelper(this)
 
   override val networks = mutableMapOf<NetworkId, Network>()
@@ -177,6 +178,8 @@ class Session(
       if (features.negotiated.hasFeature(ExtendedFeature.DccFileTransfer))
         synchronize(dccConfig, true)
       synchronize(ignoreListManager, true)
+      if (features.negotiated.hasFeature(ExtendedFeature.CoreSideHighlights))
+        synchronize(highlightRuleManager, true)
       synchronize(ircListHelper, true)
       synchronize(networkConfig, true)
 
-- 
GitLab