A window has been created for saving and loading the scanner configuration. The default configuration file is saved at %AppData%/Manufacturer/ProductName.cfg, and the user can also choose their own save path. In addition, when any setting is changed, it is automatically saved in the configuration file. The saving and loading of the scanner configuration was tested.
From: Ivan Lyugaev valy@etersoft.ru
A window has been created to save and load the scanner configuration. The default configuration file is saved in the path %AppData%/Manufacturer/ProductName.cfg, and the user can also choose their own saving path. Additionally, when any setting is changed, it is automatically saved in the configuration file. --- dlls/sane.ds/Makefile.in | 3 +- dlls/sane.ds/cfg.c | 267 +++++++++++++++++++++++++++++ dlls/sane.ds/cfg.h | 51 ++++++ dlls/sane.ds/sane_main.c | 3 + dlls/sane.ds/ui.c | 354 +++++++++++++++++++++++++++++++++++++-- 5 files changed, 662 insertions(+), 16 deletions(-) create mode 100644 dlls/sane.ds/cfg.c create mode 100644 dlls/sane.ds/cfg.h
diff --git a/dlls/sane.ds/Makefile.in b/dlls/sane.ds/Makefile.in index eed1acdf918..7bd7b82e554 100644 --- a/dlls/sane.ds/Makefile.in +++ b/dlls/sane.ds/Makefile.in @@ -12,4 +12,5 @@ SOURCES = \ sane.rc \ sane_main.c \ ui.c \ - unixlib.c + unixlib.c \ + cfg.c diff --git a/dlls/sane.ds/cfg.c b/dlls/sane.ds/cfg.c new file mode 100644 index 00000000000..1a1e3a65a62 --- /dev/null +++ b/dlls/sane.ds/cfg.c @@ -0,0 +1,267 @@ +/* +* TWAIN32 Configuration Manager +* +* Copyright 2025 Ivan Lyugaev +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#include "wine/debug.h" +#include <stdio.h> +#include <stdlib.h> + +#include "cfg.h" + +WINE_DEFAULT_DEBUG_CHANNEL(twain); + +BOOL is_exists_folder(WCHAR* path) +{ + CHAR *last_slash = wcsrchr(path, L'\'); + if (!last_slash) + { + ERR("incorrect path %s\n", debugstr_w(path)); + return FALSE; + } + *last_slash = L'\0'; + DWORD attr = GetFileAttributesW(path); + if (attr == INVALID_FILE_ATTRIBUTES) + { + if (!CreateDirectoryW(path, NULL)) + { + DWORD err = GetLastError(); + ERR("CreateDirectoryW returned error: %lu\n", err); + return FALSE; + } + } + return TRUE; +} + +BOOL is_exist_file(WCHAR* path) +{ + HANDLE hFile = CreateFileW( + path, + GENERIC_READ, + FILE_SHARE_READ, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL + ); + + if (hFile == INVALID_HANDLE_VALUE) + return FALSE; + CloseHandle(hFile); + return TRUE; +} + +BOOL save_to_file(WCHAR* path, ScannerOption *option) +{ + WCHAR folder_path[MAX_PATH]; + lstrcpynW(folder_path, path, MAX_PATH); + folder_path[MAX_PATH-1] = L'\0'; + + if (!is_exists_folder(folder_path)) + return FALSE; + + HANDLE hFile = CreateFileW( + path, + GENERIC_READ, + FILE_SHARE_READ, NULL, OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL + ); + + if (hFile == INVALID_HANDLE_VALUE) + { + ERR("CreateFileW(GENERIC_READ) returned error: %lu\n", GetLastError()); + return FALSE; + } + + CHAR buffer[1024] = {0}; + switch (option->type) + { + case TYPE_INT: + sprintf(buffer, "%s=%d\n", (option->name), option->value.int_val); + break; + case TYPE_FIXED: + sprintf(buffer, "%s=%d\n", (option->name), option->value.fixed_val); + break; + case TYPE_STRING: + sprintf(buffer, "%s=%s\n", (option->name), option->value.str_val); + break; + case TYPE_BOOL: + sprintf(buffer, "%s=%s\n", (option->name), option->value.bool_val ? "true" : "false"); + break; + } + + DWORD size = GetFileSize(hFile, NULL); + CHAR* content = (CHAR*)malloc(size + sizeof(CHAR)); + DWORD read; + ReadFile(hFile, content, size, &read, NULL); + content[size / sizeof(CHAR)] = '\0'; + CloseHandle(hFile); + + CHAR* new_content = NULL; + BOOL found = FALSE; + CHAR search[256]; + + sprintf(search, "%s=", option->name); + if (content) + { + CHAR* lines[1000] = {0}; + int count = 0; + CHAR* context = NULL; + CHAR* line = strtok_s(content, "\n", &context); + while (line && count < 1000) + { + if (!found && strstr(line, search) == line) + { + lines[count++] = buffer; + found = TRUE; + } + else + { + lines[count++] = strdup(line); + } + line = strtok_s(NULL, "\n", &context); + } + if (!found) + { + lines[count++] = buffer; + } + size_t new_size = 0; + for (int i = 0; i < count; i++) + { + new_size += strlen(lines[i]) + 1; + } + + new_content = (CHAR*)malloc(new_size * sizeof(CHAR) + 2); + new_content[0] = '\0'; + + for (int i = 0; i < count; i++) + { + strcat_s(new_content, new_size, lines[i]); + if (i < count - 1) strcat_s(new_content, new_size, "\n"); + } + for (int i = 0; i < count; i++) + { + if (lines[i] != buffer) free(lines[i]); + } + free(content); + } + else + new_content = strdup(buffer); + + hFile = CreateFileW( + path, + GENERIC_WRITE, + FILE_SHARE_READ, NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL + ); + + if (hFile == INVALID_HANDLE_VALUE) + { + DWORD err = GetLastError(); + free(new_content); + ERR("CreateFileW(GENERIC_WRITE) returned error: %lu\n", GetLastError()); + return FALSE; + } + + DWORD written; + size_t len = strlen(new_content); + WriteFile(hFile, new_content, (DWORD)len, &written, NULL); + + CloseHandle(hFile); + free(new_content); + + return TRUE; +} + +BOOL load_from_file(WCHAR* path, int type, CHAR* name, void* value) +{ + HANDLE hFile = CreateFileW( + path, + GENERIC_READ, + 0, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL + ); + if (hFile == INVALID_HANDLE_VALUE) + { + ERR("CreateFileW returned error: %lu\n", GetLastError()); + return FALSE; + } + + DWORD size = GetFileSize(hFile, NULL); + CHAR* content = (CHAR*)malloc(size + sizeof(CHAR)); + DWORD read; + ReadFile(hFile, content, size, &read, NULL); + content[size / sizeof(CHAR)] = '\0'; + CloseHandle(hFile); + + CHAR search[256]; + sprintf(search, "%s=", name); + CHAR* context = NULL; + CHAR* line = strtok_s(content, "\n", &context); + BOOL found = FALSE; + + while (line) + { + if (strstr(line, search) == line) + { + CHAR* val_start = line + strlen(search); + CHAR* space = strchr(val_start, ' '); + if (space) + *space = '\0'; + + switch (type) + { + case TYPE_INT: + *(INT*)value = atoi(val_start); + break; + case TYPE_FIXED: + *(INT*)value = atoi(val_start); + break; + case TYPE_STRING: + CHAR* out = (CHAR*)value; + strcpy((CHAR*)value, val_start); + break; + case TYPE_BOOL: + if (!strcmp(val_start, "true")) + { + *(BOOL*)value = TRUE; + } + else if (!strcmp(val_start, "false")) + { + *(BOOL*)value = FALSE; + } + else + { + return FALSE; + } + break; + default: + ERR("Unknown type: %d\n", type); + free(content); + return FALSE; + } + + found = TRUE; + break; + } + line = strtok_s(NULL, "\n", &context); + } + free(content); + return found; +} diff --git a/dlls/sane.ds/cfg.h b/dlls/sane.ds/cfg.h new file mode 100644 index 00000000000..588805341c9 --- /dev/null +++ b/dlls/sane.ds/cfg.h @@ -0,0 +1,51 @@ +/* +* TWAIN32 Configuration Manager +* +* Copyright 2025 Ivan Lyugaev +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#include <stdlib.h> +#include <stdarg.h> +#include <stdio.h> + +#include "windef.h" +#include "winbase.h" + +#include "sane_i.h" + +#define SINGLE 1 +#define MULTY 2 + +typedef struct +{ + int type; + CHAR name[64]; + int optno; + union + { + int int_val; + BOOL bool_val; + CHAR str_val[255]; + int fixed_val; + } value; + BOOL is_enabled; +} ScannerOption; + +BOOL save_to_file(WCHAR* path, ScannerOption *option); +BOOL load_from_file(WCHAR* path, int type, CHAR* name, void* value); + +BOOL is_exist_file(WCHAR* path); diff --git a/dlls/sane.ds/sane_main.c b/dlls/sane.ds/sane_main.c index 8f07ce6d52a..360509e2bf6 100644 --- a/dlls/sane.ds/sane_main.c +++ b/dlls/sane.ds/sane_main.c @@ -74,6 +74,9 @@ static TW_UINT16 SANE_OpenDS( pTW_IDENTITY pOrigin, pTW_IDENTITY self) activeDS.twCC = SANE_SaneSetDefaults(); if (activeDS.twCC == TWCC_SUCCESS) { + strcpy(activeDS.identity.Manufacturer, self->Manufacturer); + strcpy(activeDS.identity.ProductFamily, self->ProductFamily); + strcpy(activeDS.identity.ProductName, self->ProductName); activeDS.currentState = 4; activeDS.identity.Id = self->Id; activeDS.appIdentity = *pOrigin; diff --git a/dlls/sane.ds/ui.c b/dlls/sane.ds/ui.c index 25a1cf33970..bb282cce115 100644 --- a/dlls/sane.ds/ui.c +++ b/dlls/sane.ds/ui.c @@ -30,11 +30,28 @@ #include "wine/debug.h" #include "resource.h"
+#include "cfg.h" + WINE_DEFAULT_DEBUG_CHANNEL(twain);
-#define ID_BASE 0x100 -#define ID_EDIT_BASE 0x1000 -#define ID_STATIC_BASE 0x2000 +#define ID_BASE 0x100 +#define ID_EDIT_BASE 0x1000 +#define ID_STATIC_BASE 0x2000 +#define ID_APPLY_NOW 0x3021 + +#define ID_SAVE_CFG 0x8337 +#define ID_EDIT_FILENAME 0x8338 +#define ID_BUTTON_SAVE 0x8339 +#define ID_BUTTON_LOAD 0x8340 + +#define CSIDL_LOCAL_APPDATA 0x001c + +WCHAR path[MAX_PATH]; +HWND currentHWND; + +int optionsExist[128]; +int cntOptionsExist; +int gOptCount;
static INT_PTR CALLBACK DialogProc (HWND , UINT , WPARAM , LPARAM ); static INT CALLBACK PropSheetProc(HWND, UINT,LPARAM); @@ -425,6 +442,8 @@ static LPDLGTEMPLATEW create_options_page(HDC hdc, int *from_index, }
control_len += len + padding; + optionsExist[cntOptionsExist] = opt.optno; + cntOptionsExist++; }
if ( group_offset && !split_tabs ) @@ -485,6 +504,8 @@ BOOL DoScannerUI(void)
hdc = CreateCompatibleDC(0);
+ gOptCount = optcount; + while (index < optcount) { struct option_descriptor opt; @@ -508,13 +529,26 @@ BOOL DoScannerUI(void) psp[page_count].lParam = (LPARAM)&activeDS; page_count ++; } - + index ++; } - + len = lstrlenA(activeDS.identity.Manufacturer) + lstrlenA(activeDS.identity.ProductName) + 2; szCaption = malloc(len *sizeof(WCHAR)); + + WCHAR path_app_data[MAX_PATH]; + GetEnvironmentVariableW(L"LOCALAPPDATA", path_app_data, MAX_PATH); + for (int i = 0; activeDS.identity.ProductFamily[i]; i++) + { + if (activeDS.identity.ProductFamily[i] == L'/') + { + activeDS.identity.ProductFamily[i] = L'_'; + } + } + swprintf(path, MAX_PATH, L"%s\%S\%S_%S.sc", path_app_data, + activeDS.identity.Manufacturer, activeDS.identity.ProductFamily,activeDS.identity.ProductName); + MultiByteToWideChar(CP_ACP,0,activeDS.identity.Manufacturer,-1, szCaption,len); szCaption[lstrlenA(activeDS.identity.Manufacturer)] = ' '; @@ -548,6 +582,85 @@ BOOL DoScannerUI(void) return FALSE; }
+static void set_value(struct option_descriptor* opt, ScannerOption* option) +{ + CHAR title[64]; + WideCharToMultiByte(CP_UTF8, 0, opt->title, -1, title, sizeof(title), NULL, NULL); + lstrcpynA(option->name, title, 64); + option->is_enabled = opt->is_active; + option->optno = opt->optno; + + if (opt->type ==TYPE_STRING && opt->constraint_type != CONSTRAINT_NONE) + { + sane_option_get_value(opt->optno, option->value.str_val); + option->type = opt->type; + } + else if (opt->type == TYPE_BOOL) + { + option->type = opt->type; + sane_option_get_value(opt->optno, &option->value.bool_val); + } + else if (opt->type == TYPE_INT && opt->constraint_type == CONSTRAINT_WORD_LIST) + { + option->type = opt->type; + sane_option_get_value(opt->optno, &option->value.int_val); + } + else if (opt->constraint_type == CONSTRAINT_RANGE) + { + if (opt->type == TYPE_INT) + { + int si; + option->type = opt->type; + sane_option_get_value(opt->optno, &si); + if (opt->constraint.range.quant) + { + si = si / opt->constraint.range.quant; + } + option->value.int_val = si; + } + else if (opt->type == TYPE_FIXED) + { + int pos, *sf; + sf = calloc( opt->size, sizeof(int) ); + sane_option_get_value(opt->optno, sf ); + if (opt->constraint.range.quant) + pos = *sf / opt->constraint.range.quant; + else + pos = MulDiv( *sf, 100, 65536 ); + option->type = opt->type; + option->value.int_val = pos; + free(sf); + } + } +} + +static BOOL save_cfg_data(HWND hwnd, char file_path[128], int type, int optno) +{ + switch(type) + { + case SINGLE: + ScannerOption option; + struct option_descriptor opt; + opt.optno = optno; + SANE_CALL( option_get_descriptor, &opt); + + set_value(&opt, &option); + save_to_file(path, &option); + break; + case MULTY: + ScannerOption options[cntOptionsExist+1]; + for(int i = 0; i < cntOptionsExist; i++) + { + struct option_descriptor opt; + opt.optno = optionsExist[i]; + SANE_CALL( option_get_descriptor, &opt); + set_value(&opt, &options[i]); + save_to_file(path, &options[i]); + } + } + return TRUE; +} + static void UpdateRelevantEdit(HWND hwnd, const struct option_descriptor *opt, int position) { WCHAR buffer[244]; @@ -607,6 +720,7 @@ static BOOL UpdateSaneScrollOption(const struct option_descriptor *opt, DWORD po si = position;
sane_option_set_value( opt->optno, &si, &result ); + save_cfg_data(NULL, path, SINGLE, opt->optno); break; } case TYPE_FIXED: @@ -616,6 +730,7 @@ static BOOL UpdateSaneScrollOption(const struct option_descriptor *opt, DWORD po si = MulDiv( position, 65536, 100 );
sane_option_set_value( opt->optno, &si, &result ); + save_cfg_data(NULL, path, SINGLE, opt->optno); break; default: break; @@ -635,19 +750,26 @@ static INT_PTR InitializeDialog(HWND hwnd) if (rc != TWCC_SUCCESS) { ERR("Unable to read number of options\n"); - return FALSE; + optcount = gOptCount; + } + else + gOptCount = optcount; + + if (!is_exist_file(path)) + { + save_cfg_data(hwnd, path, MULTY, -1); }
for ( i = 1; i < optcount; i++) { struct option_descriptor opt; - control = GetDlgItem(hwnd,i+ID_BASE);
if (!control) continue;
opt.optno = i; + SANE_CALL( option_get_descriptor, &opt );
TRACE("%i %s %i %i\n",i,debugstr_w(opt.title),opt.type,opt.constraint_type); @@ -655,34 +777,84 @@ static INT_PTR InitializeDialog(HWND hwnd)
SendMessageA(control,CB_RESETCONTENT,0,0); /* initialize values */ + + CHAR title[64]; + WideCharToMultiByte(CP_UTF8, 0, opt.title, -1, title, sizeof(title), NULL, NULL); + if (opt.type == TYPE_STRING && opt.constraint_type != CONSTRAINT_NONE) { CHAR buffer[255]; WCHAR *p;
+ BOOL is_exist = load_from_file(path, opt.type, title, buffer); + BOOL is_correct = FALSE; + for (p = opt.constraint.strings; *p; p += lstrlenW(p) + 1) + { SendMessageW( control,CB_ADDSTRING,0, (LPARAM)p ); + CHAR param[256]; + WideCharToMultiByte(CP_UTF8, 0, p, -1, param, sizeof(param), NULL, NULL); + if (!strcmp(param, buffer)) + { + is_correct = TRUE; + } + } + + if (is_exist && is_correct) + { + sane_option_set_value(opt.optno, buffer, NULL); + } + + if (!is_correct) + { + ERR("%s=%s is incorrect. The default value is set!", title, buffer); + } + sane_option_get_value( i, buffer ); SendMessageA(control,CB_SELECTSTRING,0,(LPARAM)buffer); } else if (opt.type == TYPE_BOOL) { BOOL b; + BOOL is_exist = load_from_file(path, opt.type, title, &b); + + if (is_exist) + { + sane_option_set_value( i, &b, NULL ); + } + sane_option_get_value( i, &b ); + if (b) + { SendMessageA(control,BM_SETCHECK,BST_CHECKED,0); - + continue; + } + SendMessageA(control,BM_SETCHECK,BST_UNCHECKED,0); } else if (opt.type == TYPE_INT && opt.constraint_type == CONSTRAINT_WORD_LIST) { int j, count = opt.constraint.word_list[0]; CHAR buffer[16]; int val; + BOOL is_exist = load_from_file(path, opt.type, title, &val); + BOOL is_correct = FALSE; + for (j=1; j<=count; j++) { + if (opt.constraint.word_list[j] == val) + { + is_correct = TRUE; + } sprintf(buffer, "%d", opt.constraint.word_list[j]); SendMessageA(control, CB_ADDSTRING, 0, (LPARAM)buffer); } + if (is_exist && is_correct) + sane_option_set_value( i, &val, NULL ); + + if (!is_correct) + ERR("%s=%d is incorrect. The default value is set!\n", title, val); + sane_option_get_value( i, &val ); sprintf(buffer, "%d", val); SendMessageA(control,CB_SELECTSTRING,0,(LPARAM)buffer); @@ -693,6 +865,8 @@ static INT_PTR InitializeDialog(HWND hwnd) { int si; int min,max; + BOOL is_exist = load_from_file(path, opt.type, title, &si); + BOOL is_correct = FALSE;
min = opt.constraint.range.min / (opt.constraint.range.quant ? opt.constraint.range.quant : 1); @@ -702,7 +876,16 @@ static INT_PTR InitializeDialog(HWND hwnd)
SendMessageA(control,SBM_SETRANGE,min,max);
+ if (si >= min && si <= max) + is_correct = TRUE; + else + ERR("%s=%d is out of range [%d..%d]. The default value is used!\n", title, si, min, max); + + if (is_correct && is_exist) + sane_option_set_value( i, &si, NULL); + sane_option_get_value( i, &si ); + if (opt.constraint.range.quant) si = si / opt.constraint.range.quant;
@@ -711,8 +894,8 @@ static INT_PTR InitializeDialog(HWND hwnd) } else if (opt.type == TYPE_FIXED) { - int pos, min, max, *sf; - + int pos, min, max, *sf, val; + BOOL is_exist, is_correct = FALSE; if (opt.constraint.range.quant) { min = opt.constraint.range.min / opt.constraint.range.quant; @@ -726,6 +909,23 @@ static INT_PTR InitializeDialog(HWND hwnd)
SendMessageA(control,SBM_SETRANGE,min,max);
+ is_exist = load_from_file(path, opt.type, title, &val); + + if (val >= min && val <= max) + is_correct = TRUE; + else + ERR("%s = %d is out of range [%d..%d]. The default value is used!\n", title, val, min, max); + + if (is_exist && is_correct) + { + int valSet; + if (opt.constraint.range.quant) + valSet = val * opt.constraint.range.quant; + else + valSet = MulDiv(val, 65536, 100); + sane_option_set_value(i, &valSet, NULL); + } +
sf = calloc( opt.size, sizeof(int) ); sane_option_get_value( i, sf ); @@ -817,7 +1017,12 @@ static void ButtonClicked(HWND hwnd, INT id, HWND control) { BOOL r = SendMessageW(control,BM_GETCHECK,0,0)==BST_CHECKED; sane_option_set_value( opt.optno, &r, &changed ); - if (changed) InitializeDialog(hwnd); + + if (changed) + { + save_cfg_data(NULL, path, SINGLE, opt.optno); + InitializeDialog(hwnd); + } } }
@@ -851,10 +1056,104 @@ static void ComboChanged(HWND hwnd, INT id, HWND control) int val = atoi( value ); sane_option_set_value( opt.optno, &val, &changed ); } - if (changed) InitializeDialog(hwnd); + + if (changed) + { + save_cfg_data(NULL, path, SINGLE, opt.optno); + InitializeDialog(hwnd); + } free( value ); }
+LRESULT CALLBACK SaveWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_CREATE: + RECT rcClient; + GetClientRect(hwnd, &rcClient); + + HWND tag = CreateWindowExW(0, L"STATIC", L"Enter filename:", WS_CHILD | WS_VISIBLE, 10, 10, + rcClient.right - 20, 20, hwnd, NULL, SANE_instance, NULL); + HWND edit = CreateWindowExW(0, L"EDIT", path, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL, + 10, 40, rcClient.right - 20, 25,hwnd, (HMENU)ID_EDIT_FILENAME, SANE_instance, NULL); + int totalButtonsWidth = 210; + int x = (rcClient.right - totalButtonsWidth) / 2; + int y = 75; + HWND btnSave = CreateWindowExW(0, L"BUTTON", L"Save", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, + x, y, 100, 30, hwnd, (HMENU)ID_BUTTON_SAVE, SANE_instance, NULL); + HWND btnLoad = CreateWindowExW( 0, L"BUTTON", L"Load", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, + x+110, y, 100, 30, hwnd, (HMENU)ID_BUTTON_LOAD, SANE_instance, NULL); + SendMessageW(tag , WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), TRUE); + SendMessageW(edit , WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), TRUE); + SendMessageW(btnSave, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), TRUE); + SendMessageW(btnLoad, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), TRUE); + break; + + case WM_COMMAND: + WCHAR file_path[MAX_PATH] = {0}; + switch (LOWORD(wParam)) + { + case ID_BUTTON_SAVE: + if (save_cfg_data(currentHWND, path, MULTY, -1)) + DestroyWindow(hwnd); + break; + case ID_BUTTON_LOAD: + GetDlgItemTextW(hwnd, ID_EDIT_FILENAME, file_path, MAX_PATH); + BOOL is_exist = is_exist_file(file_path); + + if (wcscmp(path, file_path) && is_exist) + { + memset(path, 0, sizeof(path)); + lstrcpynW(path, file_path, MAX_PATH); + InitializeDialog(currentHWND); + } + if (!is_exist) + { + ERR("File %s does not exist!\n", debugstr_w(file_path)); + break; + } + DestroyWindow(hwnd); + break; + } + break; + + case WM_DESTROY: + PostQuitMessage(0); + break; + } + return DefWindowProcW(hwnd, uMsg, wParam, lParam); +} + +void ShowSaveWindow() +{ + WNDCLASSW wc = {0}; + wc.lpfnWndProc = SaveWindowProc; + wc.hInstance = SANE_instance; + wc.lpszClassName = L"SaveWindow"; + + RegisterClassW(&wc); + + HWND hwnd = CreateWindowW( + L"SaveWindow", + L"save settings", + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, + 400, 150, + NULL, NULL, + SANE_instance, NULL + ); + + ShowWindow(hwnd, SW_SHOW); + UpdateWindow(hwnd); + + MSG msg; + while (GetMessageW(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } +}
static INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { @@ -889,7 +1188,15 @@ static INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM switch (HIWORD(wParam)) { case BN_CLICKED: - ButtonClicked(hwndDlg,LOWORD(wParam), (HWND)lParam); + switch(LOWORD(wParam)) + { + case ID_SAVE_CFG: + currentHWND = hwndDlg; + ShowSaveWindow(); + break; + default: + ButtonClicked(hwndDlg,LOWORD(wParam), (HWND)lParam); + } break; case CBN_SELCHANGE: ComboChanged(hwndDlg,LOWORD(wParam), (HWND)lParam); @@ -906,8 +1213,25 @@ static int CALLBACK PropSheetProc(HWND hwnd, UINT msg, LPARAM lParam) /* rename OK button to Scan */ HWND scan = GetDlgItem(hwnd,IDOK); SetWindowTextA(scan,"Scan"); - } - return TRUE; + HWND save = GetDlgItem(hwnd, ID_APPLY_NOW); + + RECT rc; + GetWindowRect(save, &rc); + POINT pt = {rc.left, rc.top}; + ScreenToClient(hwnd, &pt); + + HWND myButton = CreateWindowExW( + 0, L"BUTTON", L"Save", + WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, + pt.x, pt.y, rc.right - rc.left, rc.bottom - rc.top, + hwnd, + ID_SAVE_CFG, + SANE_instance, + NULL); + SendMessageW(myButton, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), TRUE); + } + + return TRUE; }