Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,16 @@ class ProjectFileGroupNode(

/**
* Determines the icon for this group based on the patterns.
* - If a custom icon is set, use that
* - If there's only one pattern, use a file-type-specific icon
* - If there are multiple patterns, use a generic folder icon
*/
private fun getGroupIcon(): Icon {
// Check if a custom icon is set
if (fileGroup.customIconPath != null) {
return getIconFromPath(fileGroup.customIconPath!!)
}

if (fileGroup.patterns.size == 1) {
val pattern = fileGroup.patterns[0]
val fileTypeManager = FileTypeManager.getInstance()
Expand All @@ -86,6 +92,38 @@ class ProjectFileGroupNode(
return AllIcons.Nodes.Folder
}

/**
* Resolves an icon from the AllIcons path string.
* Example: "AllIcons.Nodes.Folder" -> AllIcons.Nodes.Folder
*/
private fun getIconFromPath(iconPath: String): Icon {
return try {
// Parse the icon path and use reflection to get the icon
when (iconPath) {
"AllIcons.Nodes.Folder" -> AllIcons.Nodes.Folder
"AllIcons.Nodes.Package" -> AllIcons.Nodes.Package
"AllIcons.Nodes.Module" -> AllIcons.Nodes.Module
"AllIcons.Nodes.ConfigFolder" -> AllIcons.Nodes.ConfigFolder
"AllIcons.Nodes.DataTables" -> AllIcons.Nodes.DataTables
"AllIcons.Nodes.ResourceBundle" -> AllIcons.Nodes.ResourceBundle
"AllIcons.Nodes.WebFolder" -> AllIcons.Nodes.WebFolder
"AllIcons.General.Settings" -> AllIcons.General.Settings
"AllIcons.General.GearPlain" -> AllIcons.General.GearPlain
"AllIcons.Actions.ListFiles" -> AllIcons.Actions.ListFiles
"AllIcons.Actions.GroupBy" -> AllIcons.Actions.GroupBy
"AllIcons.Actions.Copy" -> AllIcons.Actions.Copy
"AllIcons.Actions.Edit" -> AllIcons.Actions.Edit
"AllIcons.FileTypes.Config" -> AllIcons.FileTypes.Config
"AllIcons.FileTypes.Text" -> AllIcons.FileTypes.Text
"AllIcons.FileTypes.Properties" -> AllIcons.FileTypes.Properties
"AllIcons.FileTypes.Archive" -> AllIcons.FileTypes.Archive
else -> AllIcons.Nodes.Folder // Default fallback
}
} catch (e: Exception) {
AllIcons.Nodes.Folder // Fallback in case of error
}
}

/**
* Checks if a filename matches any of the specified patterns.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import com.intellij.util.xmlb.XmlSerializerUtil
*/
data class ProjectFileGroup(
var groupName: String = "",
var patterns: MutableList<String> = mutableListOf()
var patterns: MutableList<String> = mutableListOf(),
var customIconPath: String? = null
)

@State(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ class AndroidViewSettingsConfigurable : SearchableConfigurable {
// Clear and rebuild the groups list to ensure proper change detection
settings.projectFileGroups.clear()
groupsTableModel?.getGroups()?.forEach { group ->
settings.projectFileGroups.add(ProjectFileGroup(group.groupName, group.patterns.toMutableList()))
settings.projectFileGroups.add(ProjectFileGroup(group.groupName, group.patterns.toMutableList(), group.customIconPath))
}

// Refresh all open projects to reflect the changes
Expand All @@ -144,7 +144,7 @@ class AndroidViewSettingsConfigurable : SearchableConfigurable {

showBuildDirectoryCheckBox?.isSelected = settings.showBuildDirectory
groupsTableModel?.setGroups(settings.projectFileGroups.map {
ProjectFileGroup(it.groupName, it.patterns.toMutableList())
ProjectFileGroup(it.groupName, it.patterns.toMutableList(), it.customIconPath)
})
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.z8dn.plugins.a2pt.settings

import com.intellij.icons.AllIcons
import javax.swing.Icon

/**
* Enumeration of available icon options for file groups.
* Each option provides a display name and the corresponding icon.
*/
enum class FileGroupIconOption(
val displayName: String,
val iconPath: String?,
val icon: Icon
) {
AUTO("Auto (based on patterns)", null, AllIcons.FileTypes.Any_type),
FOLDER("Folder", "AllIcons.Nodes.Folder", AllIcons.Nodes.Folder),
PACKAGE("Package", "AllIcons.Nodes.Package", AllIcons.Nodes.Package),
MODULE("Module", "AllIcons.Nodes.Module", AllIcons.Nodes.Module),
CONFIG_FOLDER("Config Folder", "AllIcons.Nodes.ConfigFolder", AllIcons.Nodes.ConfigFolder),
DATA_TABLES("Data Tables", "AllIcons.Nodes.DataTables", AllIcons.Nodes.DataTables),
RESOURCES("Resources", "AllIcons.Nodes.ResourceBundle", AllIcons.Nodes.ResourceBundle),
WEB_FOLDER("Web Folder", "AllIcons.Nodes.WebFolder", AllIcons.Nodes.WebFolder),
SETTINGS("Settings", "AllIcons.General.Settings", AllIcons.General.Settings),
GEAR("Gear", "AllIcons.General.GearPlain", AllIcons.General.GearPlain),
LIST_FILES("List Files", "AllIcons.Actions.ListFiles", AllIcons.Actions.ListFiles),
GROUP_BY("Group By", "AllIcons.Actions.GroupBy", AllIcons.Actions.GroupBy),
COPY("Copy", "AllIcons.Actions.Copy", AllIcons.Actions.Copy),
EDIT("Edit", "AllIcons.Actions.Edit", AllIcons.Actions.Edit),
CONFIG("Config File", "AllIcons.FileTypes.Config", AllIcons.FileTypes.Config),
TEXT("Text File", "AllIcons.FileTypes.Text", AllIcons.FileTypes.Text),
PROPERTIES("Properties", "AllIcons.FileTypes.Properties", AllIcons.FileTypes.Properties),
ARCHIVE("Archive", "AllIcons.FileTypes.Archive", AllIcons.FileTypes.Archive);

companion object {
/**
* Finds the IconOption that matches the given icon path.
* Returns AUTO if no match is found.
*/
fun fromIconPath(iconPath: String?): FileGroupIconOption {
if (iconPath == null) return AUTO
return entries.find { it.iconPath == iconPath } ?: AUTO
}
}
Comment on lines +10 to +43
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Enum is well-structured, but the icon-path-to-Icon mapping is duplicated.

ProjectFileGroupNode.getIconFromPath() manually duplicates the same path→icon mapping that this enum already encapsulates. ProjectFileGroupNode should use FileGroupIconOption.fromIconPath(iconPath).icon instead, keeping this enum as the single source of truth.

This avoids the risk of the two mappings drifting out of sync when icons are added or removed.

🤖 Prompt for AI Agents
In `@src/main/kotlin/com/z8dn/plugins/a2pt/settings/FileGroupIconOption.kt` around
lines 10 - 43, ProjectFileGroupNode.getIconFromPath duplicates the iconPath→Icon
mapping already centralized in FileGroupIconOption; replace that logic with
FileGroupIconOption.fromIconPath(iconPath).icon so the enum is the single source
of truth. Update ProjectFileGroupNode.getIconFromPath to call
FileGroupIconOption.fromIconPath(iconPath) and return its .icon (handle
null/unknown by relying on fromIconPath returning AUTO). Remove the duplicated
mapping entries in ProjectFileGroupNode to avoid drift and ensure any future
icon additions are made only in FileGroupIconOption.

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,21 @@ package com.z8dn.plugins.a2pt.settings

import com.z8dn.plugins.a2pt.AndroidViewBundle

import com.intellij.openapi.ui.ComboBox
import com.intellij.openapi.ui.DialogWrapper
import com.intellij.openapi.ui.ValidationInfo
import com.intellij.ui.ToolbarDecorator
import com.intellij.ui.components.JBLabel
import com.intellij.ui.components.JBTextField
import com.intellij.ui.table.JBTable
import com.intellij.util.ui.JBUI
import java.awt.Component
import java.awt.Dimension
import java.awt.GridBagConstraints
import java.awt.GridBagLayout
import javax.swing.DefaultListCellRenderer
import javax.swing.JComponent
import javax.swing.JList
import javax.swing.JOptionPane
import javax.swing.JPanel
import javax.swing.ListSelectionModel
Expand All @@ -26,6 +30,7 @@ class ProjectFileGroupDialog(
) : DialogWrapper(true) {

private val groupNameField: JBTextField = JBTextField()
private val iconComboBox: ComboBox<FileGroupIconOption>
private val patternsTable: JBTable
private val patternsTableModel: PatternsTableModel

Expand All @@ -35,6 +40,26 @@ class ProjectFileGroupDialog(
else
AndroidViewBundle.message("dialog.ProjectFileGroup.Edit.text")

// Initialize icon combo box with custom renderer
iconComboBox = ComboBox(FileGroupIconOption.entries.toTypedArray()).apply {
renderer = object : DefaultListCellRenderer() {
override fun getListCellRendererComponent(
list: JList<*>?,
value: Any?,
index: Int,
isSelected: Boolean,
cellHasFocus: Boolean
): Component {
val component = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus)
if (value is FileGroupIconOption) {
text = value.displayName
icon = value.icon
}
return component
}
}
}

patternsTableModel = PatternsTableModel()
patternsTable = JBTable(patternsTableModel).apply {
setShowGrid(true)
Expand All @@ -46,6 +71,7 @@ class ProjectFileGroupDialog(
existingGroup?.let {
groupNameField.text = it.groupName
patternsTableModel.setPatterns(it.patterns.toMutableList())
iconComboBox.selectedItem = FileGroupIconOption.fromIconPath(it.customIconPath)
}

init()
Expand All @@ -69,6 +95,19 @@ class ProjectFileGroupDialog(
groupNameField.preferredSize = Dimension(400, groupNameField.preferredSize.height)
panel.add(groupNameField, gbc)

// Icon label
gbc.gridy++
gbc.weightx = 0.0
gbc.insets = JBUI.insets(10, 5, 5, 5)
panel.add(JBLabel(AndroidViewBundle.message("dialog.ProjectFileGroup.Icon.text")), gbc)

// Icon combo box
gbc.gridy++
gbc.weightx = 1.0
gbc.insets = JBUI.insets(5)
iconComboBox.preferredSize = Dimension(400, iconComboBox.preferredSize.height)
panel.add(iconComboBox, gbc)

// Patterns section label
gbc.gridy++
gbc.insets = JBUI.insets(15, 5, 5, 5)
Expand Down Expand Up @@ -149,9 +188,11 @@ class ProjectFileGroupDialog(
* Returns the configured ProjectFileGroup from the dialog.
*/
fun getProjectFileGroup(): ProjectFileGroup {
val selectedIcon = iconComboBox.selectedItem as? FileGroupIconOption
return ProjectFileGroup(
groupName = groupNameField.text.trim(),
patterns = patternsTableModel.getPatterns()
patterns = patternsTableModel.getPatterns(),
customIconPath = selectedIcon?.iconPath
)
}

Expand Down
1 change: 1 addition & 0 deletions src/main/resources/messages/AndroidViewBundle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ settings.Table.ColumnName.patterns=Patterns
dialog.ProjectFileGroup.Add.text=Add Custom File Group
dialog.ProjectFileGroup.Edit.text=Edit Custom File Group
dialog.ProjectFileGroup.GroupName.text=Group name:
dialog.ProjectFileGroup.Icon.text=Icon:
dialog.ProjectFileGroup.Patterns.text=File patterns:
dialog.ProjectFileGroup.Patterns.description=Examples: *.md, LICENSE, README.md, *.txt, .gitignore
dialog.ProjectFileGroup.Table.ColumnName.text=Pattern
Expand Down