Skip to content

Commit 939c022

Browse files
committed
refactor(config): Use data description instead of code duplication to change the configuration system from manual maintenance to automated processing. At the same time, it customizes the reading of ini and abandons the dependence on windows ini api
1 parent c7553d4 commit 939c022

21 files changed

+1157
-841
lines changed

include/config.h

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -508,27 +508,12 @@ void WriteConfigPomodoroTimeOptions(int* times, int count);
508508
* Notification configuration functions
509509
* ============================================================================ */
510510

511-
/**
512-
* @brief Read notification message texts (with fallbacks)
513-
*/
514-
void ReadNotificationMessagesConfig(void);
515-
516511
/**
517512
* @brief Write notification timeout
518513
* @param timeout_ms Duration (recommended: 3000-10000)
519514
*/
520515
void WriteConfigNotificationTimeout(int timeout_ms);
521516

522-
/**
523-
* @brief Read notification timeout (default: 5000ms)
524-
*/
525-
void ReadNotificationTimeoutConfig(void);
526-
527-
/**
528-
* @brief Read notification opacity (default: 100, range: 0-100)
529-
*/
530-
void ReadNotificationOpacityConfig(void);
531-
532517
/**
533518
* @brief Write notification opacity (auto-clamped to 1-100)
534519
* @param opacity Opacity (100 = fully opaque)
@@ -541,22 +526,12 @@ void WriteConfigNotificationOpacity(int opacity);
541526
*/
542527
void WriteConfigNotificationMessages(const char* timeout_msg);
543528

544-
/**
545-
* @brief Read notification type (default: CATIME)
546-
*/
547-
void ReadNotificationTypeConfig(void);
548-
549529
/**
550530
* @brief Write notification type (enum to string)
551531
* @param type Notification type
552532
*/
553533
void WriteConfigNotificationType(NotificationType type);
554534

555-
/**
556-
* @brief Read notification disabled flag (default: FALSE)
557-
*/
558-
void ReadNotificationDisabledConfig(void);
559-
560535
/**
561536
* @brief Write notification disabled setting
562537
* @param disabled TRUE to suppress all notifications
@@ -576,36 +551,18 @@ void WriteConfigLanguage(int language);
576551
*/
577552
void GetAudioFolderPath(char* path, size_t size);
578553

579-
/**
580-
* @brief Read notification sound path
581-
*
582-
* @details
583-
* Supports absolute/relative paths, "SYSTEM_BEEP", or empty (no sound).
584-
*/
585-
void ReadNotificationSoundConfig(void);
586-
587554
/**
588555
* @brief Write notification sound path (no validation)
589556
* @param sound_file Path, "SYSTEM_BEEP", or empty (UTF-8)
590557
*/
591558
void WriteConfigNotificationSound(const char* sound_file);
592559

593-
/**
594-
* @brief Read notification volume (default: 50, range: 0-100)
595-
*/
596-
void ReadNotificationVolumeConfig(void);
597-
598560
/**
599561
* @brief Write notification volume (auto-clamped to 0-100)
600562
* @param volume Volume (0=mute, 100=max)
601563
*/
602564
void WriteConfigNotificationVolume(int volume);
603565

604-
/**
605-
* @brief Read notification window position and size
606-
*/
607-
void ReadNotificationWindowConfig(void);
608-
609566
/**
610567
* @brief Write notification window position and size
611568
* @param x X position (-1 for auto)

include/config/config_defaults.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,18 +49,30 @@ typedef enum {
4949
CONFIG_TYPE_STRING,
5050
CONFIG_TYPE_INT,
5151
CONFIG_TYPE_BOOL,
52-
CONFIG_TYPE_ENUM
52+
CONFIG_TYPE_FLOAT,
53+
CONFIG_TYPE_ENUM,
54+
CONFIG_TYPE_HOTKEY,
55+
CONFIG_TYPE_CUSTOM /* Requires special handling (arrays, wchar_t, etc.) */
5356
} ConfigValueType;
5457

5558
/* ============================================================================
5659
* Configuration metadata structure
5760
* ============================================================================ */
5861

62+
/**
63+
* @brief Metadata for a single configuration item
64+
*
65+
* @details
66+
* When offset is SIZE_MAX, the item requires custom handling.
67+
* Otherwise, offset points to the field in ConfigSnapshot.
68+
*/
5969
typedef struct {
6070
const char* section;
6171
const char* key;
6272
const char* defaultValue;
6373
ConfigValueType type;
74+
size_t offset; /* offsetof(ConfigSnapshot, field), SIZE_MAX for custom */
75+
size_t size; /* sizeof(field) for strings, 0 for fixed-size types */
6476
const char* description;
6577
} ConfigItemMeta;
6678

include/config/config_loader.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ typedef struct {
103103
WORD hotkeyPauseResume;
104104
WORD hotkeyRestartTimer;
105105
WORD hotkeyCustomCountdown;
106+
WORD hotkeyToggleMilliseconds;
106107

107108
/* Recent files */
108109
RecentFile recentFiles[MAX_RECENT_FILES];

include/config/config_store.h

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/**
2+
* @file config_store.h
3+
* @brief Unified configuration store
4+
*
5+
* Single source of truth for all configuration.
6+
* Memory-first design with lazy disk I/O.
7+
*/
8+
9+
#ifndef CONFIG_STORE_H
10+
#define CONFIG_STORE_H
11+
12+
#include <windows.h>
13+
#include "config_keys.h"
14+
15+
/* ============================================================================
16+
* Configuration access API
17+
* ============================================================================ */
18+
19+
/**
20+
* @brief Initialize configuration system
21+
* @param config_path Path to config file (UTF-8)
22+
*
23+
* Loads config from disk if exists, otherwise uses defaults.
24+
* Must be called before any other config functions.
25+
*/
26+
void Config_Init(const char* config_path);
27+
28+
/**
29+
* @brief Shutdown configuration system
30+
*
31+
* Flushes any pending changes to disk and frees resources.
32+
*/
33+
void Config_Shutdown(void);
34+
35+
/**
36+
* @brief Get string value
37+
* @param key Configuration key
38+
* @return String value (do not free, valid until next Set or Shutdown)
39+
*/
40+
const char* Config_GetString(ConfigKey key);
41+
42+
/**
43+
* @brief Get integer value
44+
* @param key Configuration key
45+
* @return Integer value
46+
*/
47+
int Config_GetInt(ConfigKey key);
48+
49+
/**
50+
* @brief Get boolean value
51+
* @param key Configuration key
52+
* @return Boolean value
53+
*/
54+
BOOL Config_GetBool(ConfigKey key);
55+
56+
/**
57+
* @brief Get float value
58+
* @param key Configuration key
59+
* @return Float value
60+
*/
61+
float Config_GetFloat(ConfigKey key);
62+
63+
/**
64+
* @brief Set string value
65+
* @param key Configuration key
66+
* @param value New value (will be copied)
67+
*/
68+
void Config_SetString(ConfigKey key, const char* value);
69+
70+
/**
71+
* @brief Set integer value
72+
* @param key Configuration key
73+
* @param value New value
74+
*/
75+
void Config_SetInt(ConfigKey key, int value);
76+
77+
/**
78+
* @brief Set boolean value
79+
* @param key Configuration key
80+
* @param value New value
81+
*/
82+
void Config_SetBool(ConfigKey key, BOOL value);
83+
84+
/**
85+
* @brief Set float value
86+
* @param key Configuration key
87+
* @param value New value
88+
*/
89+
void Config_SetFloat(ConfigKey key, float value);
90+
91+
/**
92+
* @brief Flush pending changes to disk
93+
*
94+
* Normally changes are batched. Call this to force immediate write.
95+
*/
96+
void Config_Flush(void);
97+
98+
/**
99+
* @brief Reload configuration from disk
100+
*
101+
* Discards any unsaved changes and reloads from file.
102+
*/
103+
void Config_Reload(void);
104+
105+
/**
106+
* @brief Mark config as dirty (needs save)
107+
*
108+
* Called automatically by Set functions.
109+
*/
110+
void Config_MarkDirty(void);
111+
112+
/**
113+
* @brief Check if config has unsaved changes
114+
* @return TRUE if dirty
115+
*/
116+
BOOL Config_IsDirty(void);
117+
118+
/**
119+
* @brief Get config file path
120+
* @return Path string (do not free)
121+
*/
122+
const char* Config_GetPath(void);
123+
124+
/**
125+
* @brief Reset a key to its default value
126+
* @param key Configuration key
127+
*/
128+
void Config_ResetToDefault(ConfigKey key);
129+
130+
/**
131+
* @brief Reset all configuration to defaults
132+
*/
133+
void Config_ResetAll(void);
134+
135+
#endif /* CONFIG_STORE_H */

include/dialog/dialog_notification.h

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -81,24 +81,17 @@ INT_PTR CALLBACK NotificationSettingsDlgProc(HWND hwndDlg, UINT msg, WPARAM wPar
8181
* ============================================================================ */
8282

8383
/**
84-
* @note All notification configuration functions are now declared in config.h
85-
* and implemented in config_notification.c:
84+
* @note Notification configuration is loaded once at startup via ReadConfig()
85+
* into g_AppConfig. Write functions are in config_notification.c:
8686
*
87-
* Read functions:
88-
* - ReadNotificationMessagesConfig()
89-
* - ReadNotificationTimeoutConfig()
90-
* - ReadNotificationOpacityConfig()
91-
* - ReadNotificationTypeConfig()
92-
* - ReadNotificationSoundConfig()
93-
* - ReadNotificationVolumeConfig()
94-
*
95-
* Write functions:
9687
* - WriteConfigNotificationMessages(timeout_msg)
9788
* - WriteConfigNotificationTimeout(timeout_ms)
9889
* - WriteConfigNotificationOpacity(opacity)
9990
* - WriteConfigNotificationType(NotificationType type)
10091
* - WriteConfigNotificationSound(sound_file)
10192
* - WriteConfigNotificationVolume(volume)
93+
* - WriteConfigNotificationWindow(x, y, width, height)
94+
* - WriteConfigNotificationDisabled(disabled)
10295
*/
10396

10497
#endif /* DIALOG_NOTIFICATION_H */

src/config/config_applier.c

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,6 @@ static int LanguageNameToEnum(const char* langName) {
4040
if (strcmp(langName, "Japanese") == 0) return APP_LANG_JAPANESE;
4141
if (strcmp(langName, "Korean") == 0) return APP_LANG_KOREAN;
4242

43-
/* Legacy numeric format support */
44-
if (isdigit(langName[0])) {
45-
int langValue = atoi(langName);
46-
if (langValue >= 0 && langValue < APP_LANG_COUNT) {
47-
return langValue;
48-
}
49-
}
50-
5143
return APP_LANG_ENGLISH;
5244
}
5345

@@ -244,9 +236,27 @@ void ApplyNotificationSettings(const ConfigSnapshot* snapshot) {
244236
g_AppConfig.notification.display.window_width = snapshot->notificationWindowWidth;
245237
g_AppConfig.notification.display.window_height = snapshot->notificationWindowHeight;
246238

239+
/* Copy sound file path and expand %LOCALAPPDATA% placeholder if needed */
247240
strncpy(g_AppConfig.notification.sound.sound_file, snapshot->notificationSoundFile, MAX_PATH - 1);
248241
g_AppConfig.notification.sound.sound_file[MAX_PATH - 1] = '\0';
249242

243+
/* Normalize %LOCALAPPDATA% placeholder to absolute path */
244+
if (g_AppConfig.notification.sound.sound_file[0] != '\0') {
245+
const char* varToken = "%LOCALAPPDATA%";
246+
size_t tokenLen = strlen(varToken);
247+
if (_strnicmp(g_AppConfig.notification.sound.sound_file, varToken, tokenLen) == 0) {
248+
const char* localAppData = getenv("LOCALAPPDATA");
249+
if (localAppData && localAppData[0] != '\0') {
250+
char resolved[MAX_PATH] = {0};
251+
snprintf(resolved, sizeof(resolved), "%s%s",
252+
localAppData,
253+
g_AppConfig.notification.sound.sound_file + tokenLen);
254+
strncpy(g_AppConfig.notification.sound.sound_file, resolved, MAX_PATH - 1);
255+
g_AppConfig.notification.sound.sound_file[MAX_PATH - 1] = '\0';
256+
}
257+
}
258+
}
259+
250260
g_AppConfig.notification.sound.volume = snapshot->notificationSoundVolume;
251261
}
252262

src/config/config_core.c

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
* - config_loader: Reading and parsing INI files
88
* - config_applier: Applying config to global variables
99
* - config_writer: Collecting and writing config
10-
*
11-
* This file maintains backward compatibility with the original API.
1210
*/
1311
#include "config.h"
1412
#include "config/config_defaults.h"
@@ -144,26 +142,8 @@ void ReadConfig() {
144142
LOG_INFO("Configuration loading completed successfully");
145143
}
146144

147-
/**
148-
* @brief Write complete configuration to file (Coordinator)
149-
*
150-
* @details
151-
* Delegates to config_writer module.
152-
*/
153-
/* WriteConfig is now in config_writer module */
154-
/* Re-exported here for backward compatibility */
155-
156-
/**
157-
* @brief Create default configuration (Re-exported)
158-
*
159-
* @details
160-
* Implemented in config_defaults module.
161-
* Re-exported here for backward compatibility.
162-
*/
163-
/* CreateDefaultConfig is in config_defaults module */
164-
165145
/* ============================================================================
166-
* Utility functions kept for backward compatibility
146+
* Utility functions
167147
* ============================================================================ */
168148

169149
/**

0 commit comments

Comments
 (0)