-
Notifications
You must be signed in to change notification settings - Fork 0
Configuration and Persistence
Relevant source files
The Configuration and Persistence system manages two distinct categories of application data: durable user preferences and ephemeral runtime state. This dual-persistence architecture separates configuration settings that survive across installations from session-specific UI state that changes during normal operation.
This page covers the overall architecture and integration of both persistence subsystems. For detailed information about specific components, see:
- ConfigManager - Configuration loading, saving, and language discovery
- Configuration Files and Settings - config.cfg structure and Config class fields
- CacheManager - Runtime state caching, cache.json structure, and window management
For information about how configuration integrates with the UI, see Settings Dialog.
Sources:
- src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L1-L133
- src/io/github/samera2022/mouse_macros/manager/CacheManager.java L1-L62
The persistence system employs a dual-manager architecture that separates durable configuration from volatile runtime state:
flowchart TD
ConfigStatic["ConfigManager static block<br>lines 25-29"]
CacheStatic["CacheManager static field<br>line 17"]
ConfigManager["ConfigManager<br>Static Utility Class"]
CacheManager["CacheManager<br>Static Utility Class"]
ConfigObj["Config<br>User Preferences<br>lines 31-44"]
CacheObj["Cache<br>Runtime State<br>CacheManager:23-28"]
ConfigFile["config.cfg<br>CONFIG_DIR"]
CacheFile["cache.json<br>CACHE_PATH"]
FileUtil["FileUtil<br>getLocalStoragePath()"]
Gson["Gson<br>JSON Serialization"]
SettingsDialog["SettingsDialog<br>reads/writes config"]
MacroSettingsDialog["MacroSettingsDialog<br>reads/writes config"]
ExitDialog["ExitDialog<br>reads/writes cache"]
MainFrame["MainFrame<br>reads config and cache"]
MacroManager["MacroManager<br>reads config"]
ConfigStatic --> ConfigManager
CacheStatic --> CacheManager
ConfigManager --> ConfigObj
CacheManager --> CacheObj
ConfigManager --> Gson
ConfigManager --> FileUtil
CacheManager --> Gson
CacheManager --> FileUtil
ConfigObj --> ConfigFile
CacheObj --> CacheFile
FileUtil --> ConfigFile
FileUtil --> CacheFile
SettingsDialog --> ConfigManager
MacroSettingsDialog --> ConfigManager
ExitDialog --> CacheManager
MainFrame --> ConfigManager
MainFrame --> CacheManager
MacroManager --> ConfigManager
subgraph Consumers ["Consumers"]
SettingsDialog
MacroSettingsDialog
ExitDialog
MainFrame
MacroManager
end
subgraph subGraph4 ["File I/O"]
FileUtil
Gson
end
subgraph subGraph3 ["Persistence Layer"]
ConfigFile
CacheFile
end
subgraph subGraph2 ["Data Objects"]
ConfigObj
CacheObj
end
subgraph subGraph1 ["Manager Layer"]
ConfigManager
CacheManager
end
subgraph subGraph0 ["Static Initialization"]
ConfigStatic
CacheStatic
end
Sources:
- src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L14-L29
- src/io/github/samera2022/mouse_macros/manager/CacheManager.java L13-L17
- src/io/github/samera2022/mouse_macros/ui/frame/ExitDialog.java L1-L103
The system consists of two primary manager classes that work in parallel to manage different types of application state:
ConfigManager is a static utility class that manages durable user preferences. It exposes a single public static field:
| Field | Type | Purpose |
|---|---|---|
config |
Config |
Application-level settings (language, theme, hotkeys, macro settings) |
The field is initialized in a static block ConfigManager.java L25-L29
ensuring configuration is loaded before any other application code executes.
Sources: src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L14-L29
CacheManager is a static utility class that manages ephemeral runtime state. It exposes a single public static field:
| Field | Type | Purpose |
|---|---|---|
cache |
Cache |
Runtime UI state (last used directories, window sizes, exit preferences) |
The field is initialized during class loading CacheManager.java L17
loading cached state from the previous session.
Sources: src/io/github/samera2022/mouse_macros/manager/CacheManager.java L13-L17
classDiagram
class Config {
+boolean followSystemSettings
+String lang
+boolean enableDarkMode
+boolean enableDefaultStorage
+String defaultMmcStoragePath
+boolean enableQuickMode
+boolean allowLongStr
+String readjustFrameMode
+Map<String,String> keyMap
+boolean enableCustomMacroSettings
+int repeatTime
+double repeatDelay
}
class Cache {
+String lastLoadDirectory
+String lastSaveDirectory
+Map<String,String> windowSizeMap
+String defaultCloseOperation
}
class ConfigManager {
+static String CONFIG_DIR
+static Config config
+static Config loadConfig()
+static void saveConfig(Config)
+static void reloadConfig()
+static String[] getAvailableLangs()
}
class CacheManager {
+static Cache cache
+static void reloadCache()
+static void saveCache()
}
ConfigManager --> Config
CacheManager --> Cache
Sources:
- src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L31-L44
- src/io/github/samera2022/mouse_macros/manager/CacheManager.java L23-L28
| Field Name | Type | Default Value | Purpose |
|---|---|---|---|
followSystemSettings |
boolean |
true |
When enabled, synchronizes lang and enableDarkMode with OS settings |
lang |
String |
"zh_cn" |
Current language code (e.g., "en_us", "zh_cn", "ja_jp") |
enableDarkMode |
boolean |
false |
Dark theme enabled state |
enableDefaultStorage |
boolean |
false |
When true, uses defaultMmcStoragePath for file operations |
defaultMmcStoragePath |
String |
"" (empty) |
Default directory for saving/loading .mmc macro files |
enableQuickMode |
boolean |
false |
Enables quick playback mode (ignores recorded delays) |
allowLongStr |
boolean |
false |
Allows long strings in UI components |
readjustFrameMode |
String |
"STANDARDIZED" |
Frame resize mode: MIXED, STANDARDIZED, or MEMORIZED
|
keyMap |
Map<String,String> |
new HashMap<>() |
Custom hotkey assignments (e.g., "record" → "F2") |
enableCustomMacroSettings |
boolean |
false |
Enables per-macro custom settings |
repeatTime |
int |
1 |
Number of times to repeat macro playback |
repeatDelay |
double |
0 |
Delay in seconds between macro repetitions |
Sources: src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L31-L44
| Field Name | Type | Default Value | Purpose |
|---|---|---|---|
lastLoadDirectory |
String |
"" (empty) |
Last directory used for loading macro files |
lastSaveDirectory |
String |
"" (empty) |
Last directory used for saving macro files |
windowSizeMap |
Map<String,String> |
new HashMap<>() |
Stores window sizes by window name |
defaultCloseOperation |
String |
"" (empty) |
Default close behavior: exit_on_close, minimize_to_tray, or empty |
Sources: src/io/github/samera2022/mouse_macros/manager/CacheManager.java L23-L28
The system persists data to two separate JSON files in platform-specific application data directories:
The storage directory is resolved via FileUtil.getLocalStoragePath() ConfigManager.java L26
which returns platform-specific paths:
-
Windows:
%LOCALAPPDATA%/MouseMacros/ -
macOS:
~/Library/Application Support/MouseMacros/ -
Linux:
~/.local/share/MouseMacros/
The two persistence files are:
| File | Manager | Constant | Content |
|---|---|---|---|
config.cfg |
ConfigManager |
CONFIG_PATH |
User preferences (Config object serialized as JSON) |
cache.json |
CacheManager |
CACHE_PATH |
Runtime state (Cache object serialized as JSON) |
Sources:
- src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L15-L27
- src/io/github/samera2022/mouse_macros/manager/CacheManager.java L15
sequenceDiagram
participant Application Code
participant ConfigManager
participant Gson
participant FileUtil
participant File System
note over Application Code,File System: Save Configuration
Application Code->>ConfigManager: saveConfig(config)
ConfigManager->>ConfigManager: Check CONFIG_DIR exists
ConfigManager->>Gson: Create if needed (line 69-70)
Gson-->>ConfigManager: toJson(config)
ConfigManager->>FileUtil: JSON string
FileUtil->>File System: writeFile(CONFIG_PATH, json)
note over Application Code,File System: Load Configuration
Application Code->>ConfigManager: Write UTF-8 encoded JSON
ConfigManager->>FileUtil: loadConfig()
loop [File exists and valid]
FileUtil-->>ConfigManager: readFile(CONFIG_PATH)
ConfigManager->>Gson: JSON string
Gson-->>ConfigManager: fromJson(json, Config.class)
ConfigManager->>ConfigManager: Config object
ConfigManager-->>Application Code: Create new Config()
end
ConfigManager-->>Application Code: Save default config (line 53-54)
Sources: src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L49-L76
sequenceDiagram
participant Application Code
participant CacheManager
participant Gson
participant java.nio.file.Files
participant File System
note over Application Code,File System: Save Cache
Application Code->>CacheManager: saveCache()
CacheManager->>CacheManager: Get CACHE_PATH
CacheManager->>CacheManager: Ensure parent directory exists
CacheManager->>Gson: (line 42)
Gson-->>CacheManager: toJson(cache)
CacheManager->>java.nio.file.Files: JSON string
java.nio.file.Files->>File System: write(cachePath, json.getBytes())
note over Application Code,File System: Load Cache
CacheManager->>CacheManager: Write UTF-8 encoded JSON
CacheManager->>CacheManager: loadCache() during static init
loop [File exists]
CacheManager->>java.nio.file.Files: Get CACHE_PATH
java.nio.file.Files-->>CacheManager: readAllBytes(cachePath)
CacheManager->>Gson: byte array
Gson-->>CacheManager: fromJson(json, Cache.class)
CacheManager->>CacheManager: Cache object
CacheManager-->>CacheManager: Create new Cache()
end
Sources: src/io/github/samera2022/mouse_macros/manager/CacheManager.java L34-L61
Both managers ensure the parent directory exists before writing:
ConfigManager ConfigManager.java L69-L70
:
java.io.File dir = new java.io.File(CONFIG_DIR);
if (!dir.exists()) dir.mkdirs();
CacheManager CacheManager.java L42
:
if (cachePath.getParent() != null) Files.createDirectories(cachePath.getParent());
This guarantees that persistence files can be saved on first run, even when the application data directory doesn't exist.
Sources:
- src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L66-L76
- src/io/github/samera2022/mouse_macros/manager/CacheManager.java L38-L48
Configuration loading occurs during class initialization, before any application code executes. This is achieved through a static initialization block:
sequenceDiagram
participant JVM Class Loader
participant ConfigManager
participant CacheManager
participant FileUtil
participant Gson
note over JVM Class Loader,Gson: ConfigManager Static Initialization
JVM Class Loader->>ConfigManager: Load ConfigManager class
ConfigManager->>ConfigManager: Initialize Gson (line 18)
ConfigManager->>ConfigManager: Execute static block (lines 25-29)
ConfigManager->>FileUtil: getLocalStoragePath()
FileUtil-->>ConfigManager: Platform-specific path
ConfigManager->>ConfigManager: Set CONFIG_DIR and CONFIG_PATH
ConfigManager->>ConfigManager: loadConfig()
loop [config.cfg exists]
ConfigManager->>FileUtil: readFile(CONFIG_PATH)
FileUtil-->>ConfigManager: JSON content
ConfigManager->>Gson: fromJson(json, Config.class)
Gson-->>ConfigManager: Config object
ConfigManager->>ConfigManager: new Config() with defaults
ConfigManager->>ConfigManager: saveConfig(defaultConfig)
ConfigManager->>ConfigManager: Assign to static config field
note over JVM Class Loader,Gson: CacheManager Static Initialization
JVM Class Loader->>CacheManager: Load CacheManager class
CacheManager->>CacheManager: Initialize Gson (line 16)
CacheManager->>CacheManager: Initialize CACHE_PATH (line 15)
CacheManager->>FileUtil: getLocalStoragePath()
FileUtil-->>CacheManager: Platform-specific path
CacheManager->>CacheManager: loadCache() (line 17)
CacheManager->>CacheManager: Read file via Files.readAllBytes
CacheManager->>Gson: fromJson(json, Cache.class)
Gson-->>CacheManager: Cache object
CacheManager->>CacheManager: new Cache() with defaults
end
CacheManager->>CacheManager: Assign to static cache field
note over JVM Class Loader,Gson: Both managers ready for use
Sources:
- src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L18-L29
- src/io/github/samera2022/mouse_macros/manager/CacheManager.java L15-L17
Once initialized, both managers are accessed statically throughout the application:
Configuration Access:
// Direct field access
String language = ConfigManager.config.lang;
boolean darkMode = ConfigManager.config.enableDarkMode;
int repeatCount = ConfigManager.config.repeatTime;
Cache Access:
// Direct field access
String lastDir = CacheManager.cache.lastLoadDirectory;
String defaultOp = CacheManager.cache.defaultCloseOperation;
// Persistence
CacheManager.cache.lastSaveDirectory = "/path/to/dir";
CacheManager.saveCache();
Configuration access pattern appears in SettingsDialog.java L11
and MainFrame
while cache access pattern appears in ExitDialog.java L83-L88
Sources:
- src/io/github/samera2022/mouse_macros/ui/frame/SettingsDialog.java L1-L180
- src/io/github/samera2022/mouse_macros/ui/frame/ExitDialog.java L81-L100
The configuration system integrates with the operating system through the followSystemSettings flag, enabling automatic synchronization with OS preferences.
When config.followSystemSettings is true, the application queries the OS for:
-
System Language: Via
SystemUtil.getSystemLang(availableLangs) -
Dark Mode Preference: Via
SystemUtil.isSystemDarkMode()
This synchronization occurs in the SettingsDialog SettingsDialog.java L154-L168
:
flowchart TD
FollowSys["config.followSystemSettings"]
SysUtil["SystemUtil"]
GetLang["getSystemLang()"]
GetDark["isSystemDarkMode()"]
OSLang["System Language<br>Locale"]
OSDark["Windows Registry<br>AppsUseLightTheme"]
LangCombo["langCombo<br>Disabled when following"]
DarkBox["darkModeBox<br>Disabled when following"]
FollowSys --> SysUtil
GetLang --> OSLang
GetDark --> OSDark
OSLang --> LangCombo
OSDark --> DarkBox
subgraph subGraph3 ["UI State"]
LangCombo
DarkBox
end
subgraph OS ["OS"]
OSLang
OSDark
end
subgraph subGraph1 ["System Queries"]
SysUtil
GetLang
GetDark
SysUtil --> GetLang
SysUtil --> GetDark
end
subgraph Configuration ["Configuration"]
FollowSys
end
Sources: src/io/github/samera2022/mouse_macros/ui/frame/SettingsDialog.java L154-L168
The synchronization logic is implemented through an ItemListener attached to the "Follow System Settings" checkbox:
Key behaviors:
- When checked: Disables manual controls and queries system settings
- When unchecked: Re-enables manual controls, preserving last user selections
- Initial load: Executes once during dialog construction SettingsDialog.java L168
Sources: src/io/github/samera2022/mouse_macros/ui/frame/SettingsDialog.java L154-L168
The ConfigManager.getAvailableLangs() method ConfigManager.java L69-L114
detects available language files for the language selector in SettingsDialog. It handles two runtime environments:
flowchart TD
Start["getAvailableLangs()"]
CheckDevMode["Localizer.isDevMode()"]
DevPath["Read lang/ directory<br>from filesystem<br>(lines 72-81)"]
DevList["FileUtil.listFileNames('lang')"]
DevFilter["Filter *.json files<br>Remove .json extension"]
CheckProtocol["Check resource protocol"]
JarPath["JAR protocol<br>(lines 85-96)"]
FilePath["file protocol<br>(lines 97-109)"]
JarEnum["Enumerate JAR entries<br>Filter lang/*.json"]
FileList["List directory<br>Filter *.json"]
Return["Return String[] of lang codes"]
Start --> CheckDevMode
CheckDevMode --> DevPath
CheckDevMode --> CheckProtocol
DevPath --> DevList
DevList --> DevFilter
DevFilter --> Return
CheckProtocol --> JarPath
CheckProtocol --> FilePath
JarPath --> JarEnum
FilePath --> FileList
JarEnum --> Return
FileList --> Return
Sources: src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L69-L114
| Scenario | Detection Method | Implementation |
|---|---|---|
| Development mode | File system access to lang/ directory |
FileUtil.listFileNames("lang") [line 73](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/line 73) |
| JAR deployment | Enumerate JAR entries matching lang/*.json
|
JarFile.entries() iteration [lines 86-96](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/lines 86-96) |
| file:// protocol | URI-based file listing |
File.listFiles() with filter [lines 99-109](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/lines 99-109) |
All methods strip the .json extension to return language codes (e.g., "en_us", "zh_cn").
Sources: src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L69-L114
Different UI components interact with the persistence system based on their responsibilities:
SettingsDialog reads and writes configuration settings SettingsDialog.java L1-L180
:
Read Pattern:
import static io.github.samera2022.mouse_macros.manager.ConfigManager.config;
// Initialize UI from config
langCombo.setSelectedItem(config.lang);
darkModeBox.setSelected(config.enableDarkMode);
followSystemBox.setSelected(config.followSystemSettings);Write Pattern:
// Modify in-memory config
config.lang = (String) langCombo.getSelectedItem();
config.enableDarkMode = darkModeBox.isSelected();
config.followSystemSettings = followSystemBox.isSelected();
// Persist changes
ConfigManager.saveConfig(config);
Sources: src/io/github/samera2022/mouse_macros/ui/frame/SettingsDialog.java L1-L180
ExitDialog uses CacheManager to persist exit preferences ExitDialog.java L81-L100
:
Cache Write Pattern:
String op = "";
if (exitOnCloseRadio.isSelected()) op = CacheManager.EXIT_ON_CLOSE;
if (minimizeToTrayRadio.isSelected()) op = CacheManager.MINIMIZE_TO_TRAY;
if (rememberOptionBox.isSelected()) {
CacheManager.cache.defaultCloseOperation = op;
CacheManager.saveCache();
}
switch (op) {
case CacheManager.EXIT_ON_CLOSE:
System.exit(0);
break;
case CacheManager.MINIMIZE_TO_TRAY:
mf.minimizeToTray();
break;
}
The cache constants are defined in CacheManager.java L19-L21
:
| Constant | Value | Purpose |
|---|---|---|
EXIT_ON_CLOSE |
"exit_on_close" |
Application exits when closed |
MINIMIZE_TO_TRAY |
"minimize_to_tray" |
Application minimizes to system tray when closed |
UNKNOWN |
"" (empty) |
No default close behavior set |
Sources:
- src/io/github/samera2022/mouse_macros/ui/frame/ExitDialog.java L81-L100
- src/io/github/samera2022/mouse_macros/manager/CacheManager.java L19-L21
MacroManager reads configuration settings for playback behavior:
Read Pattern:
// Check execution settings
boolean quickMode = ConfigManager.config.enableQuickMode;
int repeatCount = ConfigManager.config.repeatTime;
double repeatDelay = ConfigManager.config.repeatDelay;
// Check storage settings
boolean useDefaultPath = ConfigManager.config.enableDefaultStorage;
String defaultPath = ConfigManager.config.defaultMmcStoragePath;
Sources: src/io/github/samera2022/mouse_macros/manager/MacroManager.java
ConfigManager defines constants for the readjustFrameMode setting ConfigManager.java L21-L23
:
| Constant | Value | Purpose |
|---|---|---|
RFM_MIXED |
"MIXED" |
Use memorized sizes when available, otherwise use standardized sizes |
RFM_STANDARDIZED |
"STANDARDIZED" |
Always calculate standardized window sizes based on content |
RFM_MEMORIZED |
"MEMORIZED" |
Always use memorized window sizes from CacheManager
|
CacheManager defines constants for exit behavior CacheManager.java L19-L21
:
| Constant | Value | Purpose |
|---|---|---|
EXIT_ON_CLOSE |
"exit_on_close" |
Application exits when main window is closed |
MINIMIZE_TO_TRAY |
"minimize_to_tray" |
Application minimizes to system tray when closed |
UNKNOWN |
"" (empty) |
No default close behavior configured |
These constants are used throughout the application to maintain consistency in configuration values.
Sources:
- src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L21-L23
- src/io/github/samera2022/mouse_macros/manager/CacheManager.java L19-L21
The Configuration System provides:
- Static Initialization: Configuration loaded before application code executes
-
Two-Tier Persistence: Separate files for application settings (
config.cfg) and UI state (cache.json) - System Integration: Optional synchronization with OS language and dark mode settings
- Environment Awareness: Supports both development and JAR-packaged execution
- Simple Access: Static fields enable straightforward configuration access throughout the codebase
- Automatic Defaults: Missing configuration files trigger creation of defaults with sensible values
For implementation details, see the child pages:
- ConfigManager - Core configuration management logic
- Configuration Files - File structure and field specifications
- File Chooser Configuration - UI state persistence mechanism
Sources: