More cleanup

This commit is contained in:
downthecrop 2025-10-26 15:40:32 -07:00
parent 47e545cbc7
commit 440b8d4de1
4 changed files with 571 additions and 482 deletions

View file

@ -0,0 +1,473 @@
package KondoKit
import KondoKit.pluginmanager.*
import KondoKit.views.ReflectiveEditorView
import plugin.Plugin
import plugin.PluginInfo
import plugin.PluginRepository
import rt4.GlobalJsonConfig
import java.awt.Component
import java.io.File
import java.util.*
import javax.swing.*
class ReflectiveEditorPlugin : Plugin() {
// Fields that were previously in ReflectiveEditorView
private var gitLabPlugins: List<GitLabPlugin> = listOf()
private var pluginStatuses: List<PluginInfoWithStatus> = listOf()
private var reloadPlugins = false // Flag for scheduled plugin reload to avoid crashes
private val pluginsDirectory: File = File(GlobalJsonConfig.instance.pluginsFolder)
override fun Init() {
// Initialize the plugin, fetch remote plugins
GitLabPluginFetcher.fetchGitLabPlugins { plugins ->
gitLabPlugins = plugins
// Update plugin statuses with comparison between installed and remote plugins
updatePluginStatuses()
}
}
// Plugin management functionality that was in ReflectiveEditorView
fun getGitLabPlugins(): List<GitLabPlugin> = gitLabPlugins
fun getPluginStatuses(): List<PluginInfoWithStatus> = pluginStatuses
fun updatePluginStatuses() {
val loadedPluginNames = getLoadedPluginNames()
val statuses = mutableListOf<PluginInfoWithStatus>()
// Get disabled plugin names and their versions
val disabledPluginInfo = getDisabledPluginInfo()
// Handle duplicate plugins (loaded and disabled) - delete the disabled version
handleDuplicatePlugins(loadedPluginNames, disabledPluginInfo)
// Process remote plugins
for (gitLabPlugin in gitLabPlugins) {
val pluginName = gitLabPlugin.path
// Skip KondoKit since it should always be loaded
if (isKondoKit(pluginName)) {
continue
}
val remoteVersion = gitLabPlugin.pluginProperties?.version ?: "Unknown"
val description = gitLabPlugin.pluginProperties?.description ?: "No description available"
val author = gitLabPlugin.pluginProperties?.author ?: "Unknown"
val isLoaded = loadedPluginNames.contains(pluginName)
val isDisabled = disabledPluginInfo.containsKey(pluginName)
val disabledVersion = disabledPluginInfo[pluginName]
// Check if this plugin is currently being downloaded
val existingStatus = pluginStatuses.find { it.name == pluginName }
val isDownloading = existingStatus?.isDownloading ?: false
val downloadProgress = existingStatus?.downloadProgress ?: 0
if (isLoaded) {
// Plugin is currently loaded
statuses.add(PluginInfoWithStatus(
name = pluginName,
installedVersion = remoteVersion, // We don't have the actual installed version, but we know it's loaded
remoteVersion = remoteVersion,
description = description,
author = author,
gitLabPlugin = gitLabPlugin,
isInstalled = true,
needsUpdate = false, // We assume it's up-to-date since it's loaded
isDownloading = isDownloading,
downloadProgress = downloadProgress
))
} else if (isDisabled) {
// Plugin is disabled, check if versions match
val versionsMatch = disabledVersion != null && disabledVersion == remoteVersion
if (!versionsMatch) {
// Versions don't match, show update option
statuses.add(PluginInfoWithStatus(
name = pluginName,
installedVersion = disabledVersion,
remoteVersion = remoteVersion,
description = description,
author = author,
gitLabPlugin = gitLabPlugin,
isInstalled = true, // It's installed but disabled
needsUpdate = true, // Needs update since versions don't match
isDownloading = isDownloading,
downloadProgress = downloadProgress
))
}
// If versions match, we don't add it to the list since there's no point showing it
} else {
// Plugin is not installed at all
statuses.add(PluginInfoWithStatus(
name = pluginName,
installedVersion = null,
remoteVersion = remoteVersion,
description = description,
author = author,
gitLabPlugin = gitLabPlugin,
isInstalled = false,
needsUpdate = false,
isDownloading = isDownloading,
downloadProgress = downloadProgress
))
}
}
pluginStatuses = statuses
}
// Helper method to get currently loaded plugin names
private fun getLoadedPluginNames(): Set<String> {
val loadedPluginNames = mutableSetOf<String>()
try {
// Get loaded plugins
val loadedPluginsField = PluginRepository::class.java.getDeclaredField("loadedPlugins")
loadedPluginsField.isAccessible = true
val loadedPlugins = loadedPluginsField.get(null) as HashMap<*, *>
for ((_, plugin) in loadedPlugins) {
val pluginName = getPluginDirName(plugin as Plugin)
loadedPluginNames.add(pluginName)
}
} catch (e: Exception) {
e.printStackTrace()
}
return loadedPluginNames
}
// Helper method to parse plugin.properties content
private fun parsePluginProperties(content: String): PluginProperties {
var author = "Unknown"
var version = "Unknown"
var description = "No description available"
val lines = content.split("\n")
for (line in lines) {
val parts = line.split("=")
if (parts.size == 2) {
val key = parts[0].trim()
val value = parts[1].replace("\"", "").replace("'", "").trim()
when {
key.startsWith("AUTHOR", ignoreCase = true) -> {
author = value
}
key.startsWith("VERSION", ignoreCase = true) -> {
version = value
}
key.startsWith("DESCRIPTION", ignoreCase = true) -> {
description = value
}
}
}
}
return PluginProperties(author, version, description)
}
// Helper method to get disabled plugin names and their versions
private fun getDisabledPluginInfo(): Map<String, String> {
val disabledPluginInfo = mutableMapOf<String, String>()
val disabledDir = File(pluginsDirectory, "disabled")
if (disabledDir.exists() && disabledDir.isDirectory) {
val disabledPlugins = disabledDir.listFiles { file -> file.isDirectory } ?: arrayOf()
for (pluginDir in disabledPlugins) {
try {
val propertiesFile = File(pluginDir, "plugin.properties")
if (propertiesFile.exists()) {
val content = propertiesFile.readText()
val properties = parsePluginProperties(content)
disabledPluginInfo[pluginDir.name] = properties.version
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
return disabledPluginInfo
}
// Helper method to handle duplicate plugins (loaded and disabled)
private fun handleDuplicatePlugins(loadedPluginNames: Set<String>, disabledPluginInfo: Map<String, String>) {
var needsReload = false
for (pluginName in loadedPluginNames) {
if (disabledPluginInfo.containsKey(pluginName)) {
try {
val disabledDir = File(pluginsDirectory, "disabled")
val pluginDir = File(disabledDir, pluginName)
if (pluginDir.exists() && pluginDir.isDirectory) {
if (deleteRecursively(pluginDir)) {
needsReload = true
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
// Reload plugins if we deleted any disabled duplicates
if (needsReload) {
PluginRepository.reloadPlugins()
}
}
// Helper method to delete a plugin
fun deletePlugin(pluginName: String, isDisabled: Boolean, mainPanel: Component) {
try {
val pluginDir = if (isDisabled) {
File(File(pluginsDirectory, "disabled"), pluginName)
} else {
File(pluginsDirectory, pluginName)
}
if (pluginDir.exists() && pluginDir.isDirectory) {
// Recursively delete the directory
if (deleteRecursively(pluginDir)) {
Helpers.showToast(mainPanel, "Plugin deleted successfully", JOptionPane.INFORMATION_MESSAGE)
// If we deleted a loaded plugin, schedule plugin reload
if (!isDisabled) {
reloadPlugins = true
}
// Refresh the plugin list view
SwingUtilities.invokeLater {
ReflectiveEditorView.addPlugins(ReflectiveEditorView.panel)
}
} else {
Helpers.showToast(mainPanel, "Failed to delete plugin", JOptionPane.ERROR_MESSAGE)
}
} else {
Helpers.showToast(mainPanel, "Plugin directory not found", JOptionPane.ERROR_MESSAGE)
}
} catch (e: Exception) {
e.printStackTrace()
Helpers.showToast(mainPanel, "Error deleting plugin: ${e.message}", JOptionPane.ERROR_MESSAGE)
}
}
// Helper method to recursively delete a directory
private fun deleteRecursively(file: File): Boolean {
if (file.isDirectory) {
file.listFiles()?.forEach { child ->
if (!deleteRecursively(child)) {
return false
}
}
}
return file.delete()
}
// Helper function to check if a plugin is the KondoKit plugin
private fun isKondoKit(pluginName: String): Boolean {
return pluginName == "KondoKit"
}
// Enable a plugin by moving it from disabled to main directory
fun enablePlugin(pluginName: String, mainPanel: Component) {
try {
// Source and destination directories
val disabledDir = File(pluginsDirectory, "disabled")
val sourceDir = File(disabledDir, pluginName)
val destDir = File(pluginsDirectory, pluginName)
// Check if source directory exists
if (!sourceDir.exists()) {
Helpers.showToast(mainPanel, "Plugin directory not found: ${sourceDir.absolutePath}", JOptionPane.ERROR_MESSAGE)
return
}
// Move the directory
if (sourceDir.renameTo(destDir)) {
Helpers.showToast(mainPanel, "Plugin enabled")
// Schedule plugin reload to avoid crashes
reloadPlugins = true
// Refresh the plugin list view
SwingUtilities.invokeLater {
ReflectiveEditorView.addPlugins(ReflectiveEditorView.panel)
}
} else {
Helpers.showToast(mainPanel, "Failed to enable plugin", JOptionPane.ERROR_MESSAGE)
}
} catch (e: Exception) {
e.printStackTrace()
Helpers.showToast(mainPanel, "Error enabling plugin: ${e.message}", JOptionPane.ERROR_MESSAGE)
}
}
// Toggle a plugin's enable/disable state by moving it between directories
fun togglePlugin(plugin: Plugin, pluginInfo: PluginInfo, toggleSwitch: Component, activated: Boolean, mainPanel: Component) {
try {
// Get the plugin directory name from the plugin's class package
val pluginDirName = getPluginDirName(plugin)
// Source and destination directories
val sourceDir = if (activated) {
// Moving from disabled to enabled
File(File(pluginsDirectory, "disabled"), pluginDirName)
} else {
// Moving from enabled to disabled
File(pluginsDirectory, pluginDirName)
}
val destDir = if (activated) {
// Moving to main plugins directory
File(pluginsDirectory, pluginDirName)
} else {
// Moving to disabled directory
val disabledDir = File(pluginsDirectory, "disabled")
if (!disabledDir.exists()) {
disabledDir.mkdirs()
}
File(disabledDir, pluginDirName)
}
// Check if source directory exists
if (!sourceDir.exists()) {
Helpers.showToast(mainPanel, "Plugin directory not found: ${sourceDir.absolutePath}", JOptionPane.ERROR_MESSAGE)
// Reset toggle switch to previous state
SwingUtilities.invokeLater {
if (toggleSwitch is JComponent) {
toggleSwitch.repaint()
}
}
return
}
// Move the directory
if (sourceDir.renameTo(destDir)) {
Helpers.showToast(mainPanel, if (activated) "Plugin enabled" else "Plugin disabled")
// Schedule plugin reload to avoid crashes
reloadPlugins = true
// Refresh the plugin list view
SwingUtilities.invokeLater {
ReflectiveEditorView.addPlugins(ReflectiveEditorView.panel)
}
} else {
Helpers.showToast(mainPanel, "Failed to ${if (activated) "enable" else "disable"} plugin", JOptionPane.ERROR_MESSAGE)
// Reset toggle switch to previous state
SwingUtilities.invokeLater {
if (toggleSwitch is JComponent) {
toggleSwitch.repaint()
}
}
}
} catch (e: Exception) {
e.printStackTrace()
Helpers.showToast(mainPanel, "Error toggling plugin: ${e.message}", JOptionPane.ERROR_MESSAGE)
// Reset toggle switch to previous state
SwingUtilities.invokeLater {
if (toggleSwitch is JComponent) {
toggleSwitch.repaint()
}
}
}
}
// Helper function to get plugin directory name from plugin instance
private fun getPluginDirName(plugin: Plugin): String {
// Extract the directory name from the plugin's package
// The package name is typically like "GroundItems.plugin" so we take the first part
val packageName = plugin.javaClass.`package`.name
return packageName.substringBefore(".")
}
// Check if a plugin is currently enabled
fun isPluginEnabled(plugin: Plugin, pluginInfo: PluginInfo): Boolean {
// Get the plugin directory name from the plugin's class package
val pluginDirName = getPluginDirName(plugin)
val pluginDir = File(pluginsDirectory, pluginDirName)
return pluginDir.exists() && pluginDir.isDirectory
}
// Method to start downloading a plugin
fun startPluginDownload(pluginStatus: PluginInfoWithStatus, mainPanel: Component) {
val gitLabPlugin = pluginStatus.gitLabPlugin
if (gitLabPlugin == null) {
Helpers.showToast(mainPanel, "Plugin information not available", JOptionPane.ERROR_MESSAGE)
return
}
// Update plugin status to show downloading
val updatedStatuses = pluginStatuses.map {
if (it.name == pluginStatus.name) {
it.copy(isDownloading = true, downloadProgress = 0)
} else {
it
}
}
pluginStatuses = updatedStatuses
// Refresh UI to show progress bar
SwingUtilities.invokeLater {
ReflectiveEditorView.addPlugins(ReflectiveEditorView.panel)
}
// Start the download
PluginDownloadManager.downloadPlugin(gitLabPlugin, object : PluginDownloadManager.DownloadProgressCallback {
override fun onProgress(pluginName: String, progress: Int) {
// Update progress in plugin statuses
val updatedStatuses = pluginStatuses.map {
if (it.name == pluginName) {
it.copy(downloadProgress = progress)
} else {
it
}
}
pluginStatuses = updatedStatuses
// Refresh UI to show progress
SwingUtilities.invokeLater {
ReflectiveEditorView.addPlugins(ReflectiveEditorView.panel)
}
}
override fun onComplete(pluginName: String, success: Boolean, errorMessage: String?) {
// Update plugin status
val updatedStatuses = pluginStatuses.map {
if (it.name == pluginName) {
it.copy(isDownloading = false, downloadProgress = if (success) 100 else 0)
} else {
it
}
}
pluginStatuses = updatedStatuses
// Show result to user
if (success) {
Helpers.showToast(mainPanel, "Plugin downloaded successfully!", JOptionPane.INFORMATION_MESSAGE)
// Reload plugins to make the newly downloaded plugin available
PluginRepository.reloadPlugins()
} else {
Helpers.showToast(mainPanel, "Failed to download plugin: ${errorMessage ?: "Unknown error"}", JOptionPane.ERROR_MESSAGE)
}
// Refresh UI
SwingUtilities.invokeLater {
ReflectiveEditorView.addPlugins(ReflectiveEditorView.panel)
}
}
})
}
// Getter for plugin directory - needed for the view
fun getPluginsDirectory(): File = pluginsDirectory
// Getter for reload flag - needed for the view
fun shouldReloadPlugins(): Boolean = reloadPlugins
// Setter for reload flag - needed for the view
fun setReloadPlugins(reload: Boolean) {
reloadPlugins = reload
}
}

View file

@ -22,7 +22,7 @@ object ViewConstants {
val DIMENSION_SMALL_ICON = Dimension(12, 12)
val DIMENSION_LARGE_ICON = Dimension(30, 30)
val DEFAULT_WIDGET_SIZE = Dimension(234, 50)
val PLUGIN_LIST_ITEM_SIZE = Dimension(DEFAULT_WIDGET_SIZE.width, 30)
val PLUGIN_LIST_ITEM_SIZE = Dimension(DEFAULT_WIDGET_SIZE.width, 36)
val TOGGLE_PLACEHOLDER_SIZE = Dimension(60, 24)
val TOTAL_XP_WIDGET_SIZE = Dimension(DEFAULT_WIDGET_SIZE.width, 42)
val IMAGE_SIZE = Dimension(25, 23)

View file

@ -160,7 +160,7 @@ object PluginDownloadManager {
debugLog("Input stream available for plugin: ${plugin.path}")
// Create output directory
val pluginsDir = ReflectiveEditorView.pluginsDirectory
val pluginsDir = File(rt4.GlobalJsonConfig.instance.pluginsFolder)
debugLog("Plugins directory: ${pluginsDir.absolutePath}")
// Validate plugins directory

View file

@ -5,6 +5,7 @@ import KondoKit.components.*
import KondoKit.ViewConstants
import KondoKit.views.ViewLayoutHelpers.createSearchFieldSection
import KondoKit.setFixedSize
import KondoKit.ReflectiveEditorPlugin
import KondoKit.pluginmanager.GitLabPlugin
import KondoKit.pluginmanager.GitLabPluginFetcher
import KondoKit.pluginmanager.PluginDownloadManager
@ -41,17 +42,11 @@ object ReflectiveEditorView : View {
const val VIEW_NAME = "REFLECTIVE_EDITOR_VIEW"
const val PLUGIN_LIST_VIEW = "PLUGIN_LIST"
const val PLUGIN_DETAIL_VIEW = "PLUGIN_DETAIL"
val pluginsDirectory: File = File(GlobalJsonConfig.instance.pluginsFolder)
private var gitLabPlugins: List<GitLabPlugin> = listOf()
private val reflectiveEditorPlugin = ReflectiveEditorPlugin()
private var pluginStatuses: List<PluginInfoWithStatus> = listOf()
private var searchField: SearchField? = null
// Flag for scheduled plugin reload to avoid crashes
private var reloadPlugins = false
// Helper function to check if a plugin is the KondoKit plugin
private fun isKondoKit(pluginName: String): Boolean {
return pluginName == "KondoKit"
@ -75,6 +70,8 @@ object ReflectiveEditorView : View {
get() = reflectiveEditorView ?: JPanel()
override fun createView() {
// Initialize the plugin
reflectiveEditorPlugin.Init()
createReflectiveEditorView()
}
@ -102,12 +99,7 @@ object ReflectiveEditorView : View {
cardLayout.show(mainPanel, PLUGIN_LIST_VIEW)
GitLabPluginFetcher.fetchGitLabPlugins { plugins ->
gitLabPlugins = plugins
// Update plugin statuses with comparison between installed and remote plugins
updatePluginStatuses()
// We'll update the UI when needed
}
// Plugin initialization is handled by the plugin class, gitLabPlugins will be fetched automatically
}
private fun createPluginListView(): JPanel {
@ -224,7 +216,7 @@ object ReflectiveEditorView : View {
e.printStackTrace()
}
val disabledDir = File(pluginsDirectory, "disabled")
val disabledDir = File(reflectiveEditorPlugin.getPluginsDirectory(), "disabled")
if (disabledDir.exists() && disabledDir.isDirectory) {
val disabledPlugins = disabledDir.listFiles { file -> file.isDirectory } ?: arrayOf()
@ -241,7 +233,7 @@ object ReflectiveEditorView : View {
}
if (pluginSearchText.isNotBlank()) {
val matchingPluginStatuses = pluginStatuses.filter { pluginStatus ->
val matchingPluginStatuses = reflectiveEditorPlugin.getPluginStatuses().filter { pluginStatus ->
!pluginStatus.isInstalled &&
!isKondoKit(pluginStatus.name) &&
(pluginStatus.name.contains(pluginSearchText, ignoreCase = true) ||
@ -285,6 +277,10 @@ object ReflectiveEditorView : View {
val panel = WidgetPanel(
widgetWidth = ViewConstants.PLUGIN_LIST_ITEM_SIZE.width,
widgetHeight = ViewConstants.PLUGIN_LIST_ITEM_SIZE.height,
paddingLeft = 10,
paddingRight = 10,
paddingTop = 10,
paddingBottom = 10,
addDefaultPadding = false
).apply {
layout = BorderLayout()
@ -326,7 +322,7 @@ object ReflectiveEditorView : View {
infoPanel.background = WIDGET_COLOR
infoPanel.add(nameLabel, BorderLayout.WEST)
val controlsPanel = JPanel(FlowLayout(FlowLayout.RIGHT, 5, 0))
val controlsPanel = JPanel(FlowLayout(FlowLayout.RIGHT, 5, -3))
controlsPanel.background = WIDGET_COLOR
editButton?.let { controlsPanel.add(it) }
@ -350,6 +346,10 @@ object ReflectiveEditorView : View {
val panel = WidgetPanel(
widgetWidth = ViewConstants.PLUGIN_LIST_ITEM_SIZE.width,
widgetHeight = ViewConstants.PLUGIN_LIST_ITEM_SIZE.height,
paddingLeft = 10,
paddingRight = 10,
paddingTop = 10,
paddingBottom = 10,
addDefaultPadding = false
).apply {
layout = BorderLayout()
@ -365,7 +365,7 @@ object ReflectiveEditorView : View {
toggleSwitch.setActivated(false)
toggleSwitch.onToggleListener = { activated ->
if (activated) {
enablePlugin(pluginName)
reflectiveEditorPlugin.enablePlugin(pluginName, mainPanel ?: JPanel())
}
// If trying to disable an already disabled plugin, reset the toggle
else {
@ -378,7 +378,7 @@ object ReflectiveEditorView : View {
infoPanel.background = WIDGET_COLOR
infoPanel.add(nameLabel, BorderLayout.WEST)
val controlsPanel = JPanel(FlowLayout(FlowLayout.RIGHT, 5, 0))
val controlsPanel = JPanel(FlowLayout(FlowLayout.RIGHT, 0, 0))
controlsPanel.background = WIDGET_COLOR
controlsPanel.add(toggleSwitch)
@ -398,6 +398,10 @@ object ReflectiveEditorView : View {
val panel = WidgetPanel(
widgetWidth = ViewConstants.PLUGIN_LIST_ITEM_SIZE.width,
widgetHeight = ViewConstants.PLUGIN_LIST_ITEM_SIZE.height,
paddingLeft = 10,
paddingRight = 10,
paddingTop = 10,
paddingBottom = 10,
addDefaultPadding = false
).apply {
layout = BorderLayout()
@ -453,10 +457,71 @@ object ReflectiveEditorView : View {
if (pluginStatus.isInstalled && !pluginStatus.needsUpdate) {
toggleSwitch.onToggleListener = { activated ->
// TODO: Implement enable/disable functionality
showToast(mainPanel, "Enable/disable functionality not yet implemented", JOptionPane.INFORMATION_MESSAGE)
// Reset for now since functionality not implemented
toggleSwitch.setActivated(true)
// Find the corresponding loaded plugin to use toggle functionality
val loadedPluginsField = PluginRepository::class.java.getDeclaredField("loadedPlugins")
loadedPluginsField.isAccessible = true
val loadedPlugins = loadedPluginsField.get(null) as HashMap<*, *>
var foundPlugin: Plugin? = null
var foundPluginInfo: PluginInfo? = null
for ((pluginInfo, plugin) in loadedPlugins) {
if (getPluginDirName(plugin as Plugin) == pluginStatus.name) {
foundPlugin = plugin
foundPluginInfo = pluginInfo as PluginInfo
break
}
}
if (foundPlugin != null && foundPluginInfo != null) {
reflectiveEditorPlugin.togglePlugin(foundPlugin, foundPluginInfo, toggleSwitch, activated, mainPanel ?: JPanel())
} else {
// Fallback for plugins that don't have loaded instances available
val pluginDirName = pluginStatus.name
val sourceDir = if (activated) {
// Moving from disabled to enabled
File(File(reflectiveEditorPlugin.getPluginsDirectory(), "disabled"), pluginDirName)
} else {
// Moving from enabled to disabled
File(reflectiveEditorPlugin.getPluginsDirectory(), pluginDirName)
}
val destDir = if (activated) {
// Moving to main plugins directory
File(reflectiveEditorPlugin.getPluginsDirectory(), pluginDirName)
} else {
// Moving to disabled directory
val disabledDir = File(reflectiveEditorPlugin.getPluginsDirectory(), "disabled")
if (!disabledDir.exists()) {
disabledDir.mkdirs()
}
File(disabledDir, pluginDirName)
}
// Check if source directory exists
if (!sourceDir.exists()) {
showToast(mainPanel, "Plugin directory not found: ${sourceDir.absolutePath}", JOptionPane.ERROR_MESSAGE)
// Reset toggle switch to previous state
toggleSwitch.setActivated(!activated)
} else {
// Move the directory
if (sourceDir.renameTo(destDir)) {
showToast(mainPanel, if (activated) "Plugin enabled" else "Plugin disabled")
// Schedule plugin reload to avoid crashes
reflectiveEditorPlugin.setReloadPlugins(true)
// Refresh the plugin list view
SwingUtilities.invokeLater {
addPlugins(reflectiveEditorView!!)
}
} else {
showToast(mainPanel, "Failed to ${if (activated) "enable" else "disable"} plugin", JOptionPane.ERROR_MESSAGE)
// Reset toggle switch to previous state
toggleSwitch.setActivated(!activated)
}
}
}
}
} else {
// Hide the toggle switch for non-installed plugins
@ -468,7 +533,7 @@ object ReflectiveEditorView : View {
infoPanel.background = WIDGET_COLOR
infoPanel.add(nameLabel, BorderLayout.WEST)
val controlsPanel = JPanel(FlowLayout(FlowLayout.RIGHT, 5, 0))
val controlsPanel = JPanel(FlowLayout(FlowLayout.RIGHT, 0, 0))
controlsPanel.background = WIDGET_COLOR
controlsPanel.add(actionButton)
controlsPanel.add(progressBar)
@ -524,60 +589,22 @@ object ReflectiveEditorView : View {
.replace(">", "&gt;")
}
private fun enablePlugin(pluginName: String) {
try {
// Source and destination directories
val disabledDir = File(pluginsDirectory, "disabled")
val sourceDir = File(disabledDir, pluginName)
val destDir = File(pluginsDirectory, pluginName)
// Check if source directory exists
if (!sourceDir.exists()) {
showToast(mainPanel, "Plugin directory not found: ${sourceDir.absolutePath}", JOptionPane.ERROR_MESSAGE)
return
}
// Move the directory
if (sourceDir.renameTo(destDir)) {
showToast(mainPanel, "Plugin enabled")
// Schedule plugin reload to avoid crashes
reloadPlugins = true
// Refresh the plugin list view
SwingUtilities.invokeLater {
addPlugins(reflectiveEditorView!!)
}
} else {
showToast(mainPanel, "Failed to enable plugin", JOptionPane.ERROR_MESSAGE)
}
} catch (e: Exception) {
e.printStackTrace()
showToast(mainPanel, "Error enabling plugin: ${e.message}", JOptionPane.ERROR_MESSAGE)
}
}
private fun createToggleSwitch(plugin: Plugin, pluginInfo: PluginInfo): ToggleSwitch {
val toggleSwitch = ToggleSwitch()
// Set initial state
toggleSwitch.setActivated(isPluginEnabled(plugin, pluginInfo))
toggleSwitch.setActivated(reflectiveEditorPlugin.isPluginEnabled(plugin, pluginInfo))
// Add toggle listener
toggleSwitch.onToggleListener = { activated ->
togglePlugin(plugin, pluginInfo, toggleSwitch, activated)
reflectiveEditorPlugin.togglePlugin(plugin, pluginInfo, toggleSwitch, activated, mainPanel ?: JPanel())
}
return toggleSwitch
}
private fun isPluginEnabled(plugin: Plugin, pluginInfo: PluginInfo): Boolean {
// Get the plugin directory name from the plugin's class package
val pluginDirName = getPluginDirName(plugin)
val pluginDir = File(pluginsDirectory, pluginDirName)
return pluginDir.exists() && pluginDir.isDirectory
}
private fun getPluginDirName(plugin: Plugin): String {
// Extract the directory name from the plugin's package
// The package name is typically like "GroundItems.plugin" so we take the first part
@ -585,64 +612,6 @@ object ReflectiveEditorView : View {
return packageName.substringBefore(".")
}
private fun togglePlugin(plugin: Plugin, pluginInfo: PluginInfo, toggleSwitch: ToggleSwitch, activated: Boolean) {
try {
// Get the plugin directory name from the plugin's class package
val pluginDirName = getPluginDirName(plugin)
// Source and destination directories
val sourceDir = if (activated) {
// Moving from disabled to enabled
File(File(pluginsDirectory, "disabled"), pluginDirName)
} else {
// Moving from enabled to disabled
File(pluginsDirectory, pluginDirName)
}
val destDir = if (activated) {
// Moving to main plugins directory
File(pluginsDirectory, pluginDirName)
} else {
// Moving to disabled directory
val disabledDir = File(pluginsDirectory, "disabled")
if (!disabledDir.exists()) {
disabledDir.mkdirs()
}
File(disabledDir, pluginDirName)
}
// Check if source directory exists
if (!sourceDir.exists()) {
showToast(mainPanel, "Plugin directory not found: ${sourceDir.absolutePath}", JOptionPane.ERROR_MESSAGE)
// Reset toggle switch to previous state
toggleSwitch.setActivated(!activated)
return
}
// Move the directory
if (sourceDir.renameTo(destDir)) {
showToast(mainPanel, if (activated) "Plugin enabled" else "Plugin disabled")
// Schedule plugin reload to avoid crashes
reloadPlugins = true
// Refresh the plugin list view
SwingUtilities.invokeLater {
addPlugins(reflectiveEditorView!!)
}
} else {
showToast(mainPanel, "Failed to ${if (activated) "enable" else "disable"} plugin", JOptionPane.ERROR_MESSAGE)
// Reset toggle switch to previous state
toggleSwitch.setActivated(!activated)
}
} catch (e: Exception) {
e.printStackTrace()
showToast(mainPanel, "Error toggling plugin: ${e.message}", JOptionPane.ERROR_MESSAGE)
// Reset toggle switch to previous state
toggleSwitch.setActivated(!activated)
}
}
private fun showPluginDetails(pluginInfo: PluginInfo, plugin: Plugin) {
currentPluginInfo = pluginInfo
currentPlugin = plugin
@ -775,13 +744,13 @@ object ReflectiveEditorView : View {
}
// Check if we need to reload plugins
if (reloadPlugins) {
if (reflectiveEditorPlugin.shouldReloadPlugins()) {
PluginRepository.reloadPlugins()
reloadPlugins = false
reflectiveEditorPlugin.setReloadPlugins(false)
}
// Update plugin statuses to reflect current loaded plugins
updatePluginStatuses()
reflectiveEditorPlugin.updatePluginStatuses()
val contentPanel = pluginListContentPanel
if (contentPanel == null) {
@ -815,104 +784,6 @@ object ReflectiveEditorView : View {
mainPanel.revalidate()
mainPanel.repaint()
}
var customToolTipWindow: JWindow? = null
fun showCustomToolTip(text: String, component: JComponent) {
val tooltipFont = ViewConstants.FONT_RUNESCAPE_SMALL_PLAIN_16
val maxWidth = 150
// Create a dummy JLabel to get FontMetrics for the font used in the tooltip
val dummyLabel = JLabel()
dummyLabel.font = tooltipFont
val fontMetrics = dummyLabel.getFontMetrics(tooltipFont)
val lineHeight = fontMetrics.height
// Calculate the approximate width of the text
val textWidth = fontMetrics.stringWidth(text)
// Calculate the number of lines required based on the text width and max tooltip width
val numberOfLines = ceil(textWidth.toDouble() / maxWidth).toInt()
// Calculate the required height of the tooltip
val requiredHeight = numberOfLines * lineHeight + 6 // Adding some padding
if (customToolTipWindow == null) {
customToolTipWindow = JWindow().apply {
val bgColor = Helpers.colorToHex(TOOLTIP_BACKGROUND)
val textColor = Helpers.colorToHex(secondaryColor)
contentPane = JLabel("<html><div style='color: $textColor; background-color: $bgColor; padding: 3px; word-break: break-all;'>$text</div></html>").apply {
border = BorderFactory.createLineBorder(Color.BLACK)
isOpaque = true
background = TOOLTIP_BACKGROUND
foreground = Color.WHITE
font = tooltipFont
maximumSize = Dimension(maxWidth, Int.MAX_VALUE)
preferredSize = Dimension(maxWidth, requiredHeight)
}
pack()
}
} else {
// Update the tooltip text
val label = customToolTipWindow!!.contentPane as JLabel
val bgColor = Helpers.colorToHex(TOOLTIP_BACKGROUND)
val textColor = Helpers.colorToHex(secondaryColor)
label.text = "<html><div style='color: $textColor; background-color: $bgColor; padding: 3px; word-break: break-all;'>$text</div></html>"
label.preferredSize = Dimension(maxWidth, requiredHeight)
customToolTipWindow!!.pack()
}
// Position the tooltip near the component
val locationOnScreen = component.locationOnScreen
customToolTipWindow!!.setLocation(locationOnScreen.x, locationOnScreen.y + 15)
customToolTipWindow!!.isVisible = true
}
// Add this helper function to read plugin properties from a disabled plugin directory
private fun readDisabledPluginProperties(pluginName: String): PluginProperties? {
try {
val disabledDir = File(pluginsDirectory, "disabled")
val pluginDir = File(disabledDir, pluginName)
val propertiesFile = File(pluginDir, "plugin.properties")
if (propertiesFile.exists()) {
val content = propertiesFile.readText()
return parsePluginProperties(content)
}
} catch (e: Exception) {
}
return null
}
// Helper function to parse plugin.properties content
private fun parsePluginProperties(content: String): PluginProperties {
var author = "Unknown"
var version = "Unknown"
var description = "No description available"
val lines = content.split("\n")
for (line in lines) {
val parts = line.split("=")
if (parts.size == 2) {
val key = parts[0].trim()
val value = parts[1].replace("\"", "").replace("'", "").trim()
when {
key.startsWith("AUTHOR", ignoreCase = true) -> {
author = value
}
key.startsWith("VERSION", ignoreCase = true) -> {
version = value
}
key.startsWith("DESCRIPTION", ignoreCase = true) -> {
description = value
}
}
}
}
return PluginProperties(author, version, description)
}
// Helper method to find and reset ScrollablePanel components within a container
private fun findAndResetScrollablePanel(container: Component) {
@ -925,234 +796,9 @@ object ReflectiveEditorView : View {
}
}
// Helper method to get currently loaded plugin names
private fun getLoadedPluginNames(): Set<String> {
val loadedPluginNames = mutableSetOf<String>()
try {
// Get loaded plugins
val loadedPluginsField = PluginRepository::class.java.getDeclaredField("loadedPlugins")
loadedPluginsField.isAccessible = true
val loadedPlugins = loadedPluginsField.get(null) as HashMap<*, *>
for ((_, plugin) in loadedPlugins) {
val pluginName = getPluginDirName(plugin as Plugin)
loadedPluginNames.add(pluginName)
}
} catch (e: Exception) {
e.printStackTrace()
}
return loadedPluginNames
}
// Method to start downloading a plugin
private fun startPluginDownload(pluginStatus: PluginInfoWithStatus) {
val gitLabPlugin = pluginStatus.gitLabPlugin
if (gitLabPlugin == null) {
showToast(mainPanel, "Plugin information not available", JOptionPane.ERROR_MESSAGE)
return
}
// Log the download URL for debugging
val downloadUrl = PluginDownloadManager.getDownloadUrlForLogging(gitLabPlugin)
// Update plugin status to show downloading
val updatedStatuses = pluginStatuses.map {
if (it.name == pluginStatus.name) {
it.copy(isDownloading = true, downloadProgress = 0)
} else {
it
}
}
pluginStatuses = updatedStatuses
// Refresh UI to show progress bar
SwingUtilities.invokeLater {
addPlugins(reflectiveEditorView!!)
}
// Start the download
PluginDownloadManager.downloadPlugin(gitLabPlugin, object : PluginDownloadManager.DownloadProgressCallback {
override fun onProgress(pluginName: String, progress: Int) {
// Update progress in plugin statuses
val updatedStatuses = pluginStatuses.map {
if (it.name == pluginName) {
it.copy(downloadProgress = progress)
} else {
it
}
}
pluginStatuses = updatedStatuses
// Refresh UI to show progress
SwingUtilities.invokeLater {
addPlugins(reflectiveEditorView!!)
}
}
override fun onComplete(pluginName: String, success: Boolean, errorMessage: String?) {
// Update plugin status
val updatedStatuses = pluginStatuses.map {
if (it.name == pluginName) {
it.copy(isDownloading = false, downloadProgress = if (success) 100 else 0)
} else {
it
}
}
pluginStatuses = updatedStatuses
// Show result to user
if (success) {
showToast(mainPanel, "Plugin downloaded successfully!", JOptionPane.INFORMATION_MESSAGE)
// Reload plugins to make the newly downloaded plugin available
PluginRepository.reloadPlugins()
} else {
showToast(mainPanel, "Failed to download plugin: ${errorMessage ?: "Unknown error"}", JOptionPane.ERROR_MESSAGE)
}
// Refresh UI
SwingUtilities.invokeLater {
addPlugins(reflectiveEditorView!!)
}
}
})
}
// Update plugin statuses by comparing installed and remote plugins
private fun updatePluginStatuses() {
val loadedPluginNames = getLoadedPluginNames()
val statuses = mutableListOf<PluginInfoWithStatus>()
// Get disabled plugin names and their versions
val disabledPluginInfo = getDisabledPluginInfo()
// Handle duplicate plugins (loaded and disabled) - delete the disabled version
handleDuplicatePlugins(loadedPluginNames, disabledPluginInfo)
// Process remote plugins
for (gitLabPlugin in gitLabPlugins) {
val pluginName = gitLabPlugin.path
// Skip KondoKit since it should always be loaded
if (isKondoKit(pluginName)) {
continue
}
val remoteVersion = gitLabPlugin.pluginProperties?.version ?: "Unknown"
val description = gitLabPlugin.pluginProperties?.description ?: "No description available"
val author = gitLabPlugin.pluginProperties?.author ?: "Unknown"
val isLoaded = loadedPluginNames.contains(pluginName)
val isDisabled = disabledPluginInfo.containsKey(pluginName)
val disabledVersion = disabledPluginInfo[pluginName]
// Check if this plugin is currently being downloaded
val existingStatus = pluginStatuses.find { it.name == pluginName }
val isDownloading = existingStatus?.isDownloading ?: false
val downloadProgress = existingStatus?.downloadProgress ?: 0
if (isLoaded) {
// Plugin is currently loaded
statuses.add(PluginInfoWithStatus(
name = pluginName,
installedVersion = remoteVersion, // We don't have the actual installed version, but we know it's loaded
remoteVersion = remoteVersion,
description = description,
author = author,
gitLabPlugin = gitLabPlugin,
isInstalled = true,
needsUpdate = false, // We assume it's up-to-date since it's loaded
isDownloading = isDownloading,
downloadProgress = downloadProgress
))
} else if (isDisabled) {
// Plugin is disabled, check if versions match
val versionsMatch = disabledVersion != null && disabledVersion == remoteVersion
if (!versionsMatch) {
// Versions don't match, show update option
statuses.add(PluginInfoWithStatus(
name = pluginName,
installedVersion = disabledVersion,
remoteVersion = remoteVersion,
description = description,
author = author,
gitLabPlugin = gitLabPlugin,
isInstalled = true, // It's installed but disabled
needsUpdate = true, // Needs update since versions don't match
isDownloading = isDownloading,
downloadProgress = downloadProgress
))
}
// If versions match, we don't add it to the list since there's no point showing it
} else {
// Plugin is not installed at all
statuses.add(PluginInfoWithStatus(
name = pluginName,
installedVersion = null,
remoteVersion = remoteVersion,
description = description,
author = author,
gitLabPlugin = gitLabPlugin,
isInstalled = false,
needsUpdate = false,
isDownloading = isDownloading,
downloadProgress = downloadProgress
))
}
}
pluginStatuses = statuses
}
// Helper method to handle duplicate plugins (loaded and disabled)
private fun handleDuplicatePlugins(loadedPluginNames: Set<String>, disabledPluginInfo: Map<String, String>) {
var needsReload = false
for (pluginName in loadedPluginNames) {
if (disabledPluginInfo.containsKey(pluginName)) {
try {
val disabledDir = File(pluginsDirectory, "disabled")
val pluginDir = File(disabledDir, pluginName)
if (pluginDir.exists() && pluginDir.isDirectory) {
if (deleteRecursively(pluginDir)) {
needsReload = true
} else {
}
}
} catch (e: Exception) {
}
}
}
// Reload plugins if we deleted any disabled duplicates
if (needsReload) {
PluginRepository.reloadPlugins()
}
}
// Helper method to get disabled plugin names and their versions
private fun getDisabledPluginInfo(): Map<String, String> {
val disabledPluginInfo = mutableMapOf<String, String>()
val disabledDir = File(pluginsDirectory, "disabled")
if (disabledDir.exists() && disabledDir.isDirectory) {
val disabledPlugins = disabledDir.listFiles { file -> file.isDirectory } ?: arrayOf()
for (pluginDir in disabledPlugins) {
try {
val propertiesFile = File(pluginDir, "plugin.properties")
if (propertiesFile.exists()) {
val content = propertiesFile.readText()
val properties = parsePluginProperties(content)
disabledPluginInfo[pluginDir.name] = properties.version
}
} catch (e: Exception) {
}
}
}
return disabledPluginInfo
reflectiveEditorPlugin.startPluginDownload(pluginStatus, mainPanel ?: JPanel())
}
// Helper method to add context menu to a panel
@ -1187,37 +833,7 @@ object ReflectiveEditorView : View {
// Helper method to delete a plugin
private fun deletePlugin(pluginName: String, isDisabled: Boolean) {
try {
val pluginDir = if (isDisabled) {
File(File(pluginsDirectory, "disabled"), pluginName)
} else {
File(pluginsDirectory, pluginName)
}
if (pluginDir.exists() && pluginDir.isDirectory) {
// Recursively delete the directory
if (deleteRecursively(pluginDir)) {
showToast(mainPanel, "Plugin deleted successfully", JOptionPane.INFORMATION_MESSAGE)
// If we deleted a loaded plugin, schedule plugin reload
if (!isDisabled) {
reloadPlugins = true
}
// Refresh the plugin list view
SwingUtilities.invokeLater {
addPlugins(reflectiveEditorView!!)
}
} else {
showToast(mainPanel, "Failed to delete plugin", JOptionPane.ERROR_MESSAGE)
}
} else {
showToast(mainPanel, "Plugin directory not found", JOptionPane.ERROR_MESSAGE)
}
} catch (e: Exception) {
e.printStackTrace()
showToast(mainPanel, "Error deleting plugin: ${e.message}", JOptionPane.ERROR_MESSAGE)
}
reflectiveEditorPlugin.deletePlugin(pluginName, isDisabled, mainPanel ?: JPanel())
}
// Helper method to recursively delete a directory