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; }