From: Bernd Herd codeberg@herdsoft.com
This is neccessary according to the Twain specification and also allows much more comfortable user interfaces. --- dlls/sane.ds/ds_ctrl.c | 34 ++++++---- dlls/sane.ds/ds_image.c | 3 + dlls/sane.ds/sane.rc | 2 +- dlls/sane.ds/sane_i.h | 9 +++ dlls/sane.ds/sane_main.c | 17 ++++- dlls/sane.ds/ui.c | 138 +++++++++++++++++++++++++++++++++++---- 6 files changed, 175 insertions(+), 28 deletions(-)
diff --git a/dlls/sane.ds/ds_ctrl.c b/dlls/sane.ds/ds_ctrl.c index 64f505d7712..13055b9c30e 100644 --- a/dlls/sane.ds/ds_ctrl.c +++ b/dlls/sane.ds/ds_ctrl.c @@ -186,6 +186,10 @@ TW_UINT16 SANE_ProcessEvent (pTW_IDENTITY pOrigin, twRC = TWRC_FAILURE; activeDS.twCC = TWCC_SEQERROR; } + else if (UI_IsDialogMessage(pMsg)) + { + twRC = TWRC_DSEVENT; + }
return twRC; } @@ -218,8 +222,6 @@ TW_UINT16 SANE_PendingXfersEndXfer (pTW_IDENTITY pOrigin, pPendingXfers->Count = 0; activeDS.currentState = 5; SANE_Cancel(); - /* Notify the application that it can close the data source */ - SANE_Notify(MSG_CLOSEDSREQ); } else { @@ -235,8 +237,6 @@ TW_UINT16 SANE_PendingXfersEndXfer (pTW_IDENTITY pOrigin, pPendingXfers->Count = 0; activeDS.currentState = 5; SANE_Cancel(); - /* Notify the application that it can close the data source */ - SANE_Notify(MSG_CLOSEDSREQ); } } twRC = TWRC_SUCCESS; @@ -291,7 +291,6 @@ TW_UINT16 SANE_PendingXfersGet (pTW_IDENTITY pOrigin, pPendingXfers->Count = 0; activeDS.currentState = 5; SANE_Cancel(); - SANE_Notify(MSG_CLOSEDSREQ); } twRC = TWRC_SUCCESS; activeDS.twCC = TWCC_SUCCESS; @@ -378,6 +377,8 @@ TW_UINT16 SANE_DisableDSUserInterface (pTW_IDENTITY pOrigin, } else { + UI_Destroy(); + activeDS.currentState = 4; twRC = TWRC_SUCCESS; activeDS.twCC = TWCC_SUCCESS; @@ -405,19 +406,28 @@ TW_UINT16 SANE_EnableDSUserInterface (pTW_IDENTITY pOrigin, { activeDS.hwndOwner = pUserInterface->hParent; activeDS.ShowUI = pUserInterface->ShowUI; + activeDS.ModalUI = FALSE; if (pUserInterface->ShowUI) { - BOOL rc; activeDS.currentState = 5; /* Transitions to state 5 */ - rc = DoScannerUI(); - pUserInterface->ModalUI = TRUE; - if (!rc) + if (!DoScannerUI()) { - SANE_Notify(MSG_CLOSEDSREQ); + twRC = TWRC_FAILURE; + activeDS.twCC = TWCC_BUMMER; } - else + + /* Since Twain 1.9, the ModalUI value set by the application has a meaning, + * before that, that struct member was only used for DS -> App. */ + activeDS.ModalUI = pUserInterface->ModalUI + && activeDS.hwndOwner + && (pOrigin->ProtocolMajor * 100 + pOrigin->ProtocolMinor)>=109 + && IsWindowEnabled(activeDS.hwndOwner); + + pUserInterface->ModalUI = activeDS.ModalUI; + + if (activeDS.ModalUI) { - get_sane_params( &activeDS.frame_params ); + EnableWindow(activeDS.hwndOwner, FALSE); } } else diff --git a/dlls/sane.ds/ds_image.c b/dlls/sane.ds/ds_image.c index f2b0a294531..0486b191f1e 100644 --- a/dlls/sane.ds/ds_image.c +++ b/dlls/sane.ds/ds_image.c @@ -125,6 +125,7 @@ TW_UINT16 SANE_Start(void) /* If starting the scan failed, cancel scan job */ if (twRC != TWRC_SUCCESS) { + UI_Enable(TRUE); activeDS.progressWnd = ScanningDialogBox(activeDS.progressWnd, -1); activeDS.twCC = TWCC_OPERATIONERROR; return TWRC_FAILURE; @@ -134,6 +135,7 @@ TW_UINT16 SANE_Start(void) { WARN("sane_get_parameters failed\n"); SANE_CALL( cancel_device, NULL ); + UI_Enable(TRUE); activeDS.progressWnd = ScanningDialogBox(activeDS.progressWnd, -1); activeDS.twCC = TWCC_OPERATIONERROR; return TWRC_FAILURE; @@ -177,6 +179,7 @@ TW_UINT16 SANE_Start(void) void SANE_Cancel(void) { SANE_CALL( cancel_device, NULL ); + UI_Enable(TRUE); activeDS.progressWnd = ScanningDialogBox(activeDS.progressWnd, -1); activeDS.currentState = 5; } diff --git a/dlls/sane.ds/sane.rc b/dlls/sane.ds/sane.rc index d853fade567..4d94aa05a39 100644 --- a/dlls/sane.ds/sane.rc +++ b/dlls/sane.ds/sane.rc @@ -50,7 +50,7 @@ END
IDD_SCANNING DIALOG 53, 50, 186, 104 -STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_VISIBLE | DS_CENTER | DS_SETFOREGROUND +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_CENTER | DS_SETFOREGROUND CAPTION "Scanning" FONT 8, "MS Shell Dlg" BEGIN diff --git a/dlls/sane.ds/sane_i.h b/dlls/sane.ds/sane_i.h index 8a1a742605e..b2b4349ac26 100644 --- a/dlls/sane.ds/sane_i.h +++ b/dlls/sane.ds/sane_i.h @@ -68,6 +68,12 @@ struct tagActiveDS
/* TRUE if user interface dialog is shown in DG_CONTROL/DAT_USERINTERFACE/MSG_ENABLEDS */ BOOL ShowUI; + + /* Data strored for the property sheet dialog while it is displayed */ + struct SUiData * ui_data; + + /* TRUE if the DS makes the parent window Modal */ + BOOL ModalUI; };
extern struct tagActiveDS activeDS; @@ -213,6 +219,9 @@ TW_UINT16 SANE_RGBResponseSet /* UI function */ BOOL DoScannerUI(void); HWND ScanningDialogBox(HWND dialog, LONG progress); +BOOL UI_IsDialogMessage(MSG *msg); +void UI_Destroy(void); +void UI_Enable(BOOL enable);
/* Option functions */ TW_UINT16 sane_option_get_value( int optno, void *val ); diff --git a/dlls/sane.ds/sane_main.c b/dlls/sane.ds/sane_main.c index 6b8f1e23f89..ad9268f14ad 100644 --- a/dlls/sane.ds/sane_main.c +++ b/dlls/sane.ds/sane_main.c @@ -95,6 +95,21 @@ static TW_UINT16 SANE_OpenDS( pTW_IDENTITY pOrigin, pTW_IDENTITY self)
static TW_UINT16 SANE_SetEntryPoint (pTW_IDENTITY pOrigin, TW_MEMREF pData);
+ +/** @brief Close the data source. + * Closes all associated windows and frees memory. + */ +static void SANE_CloseDS(void) +{ + if(activeDS.progressWnd) + { + ScanningDialogBox(activeDS.progressWnd, -1); + } + SANE_CALL( close_ds, NULL ); + UI_Destroy(); +} + + static TW_UINT16 SANE_SourceControlHandler ( pTW_IDENTITY pOrigin, TW_UINT16 DAT, @@ -109,7 +124,7 @@ static TW_UINT16 SANE_SourceControlHandler ( switch (MSG) { case MSG_CLOSEDS: - SANE_CALL( close_ds, NULL ); + SANE_CloseDS(); break; case MSG_OPENDS: twRC = SANE_OpenDS( pOrigin, (pTW_IDENTITY)pData); diff --git a/dlls/sane.ds/ui.c b/dlls/sane.ds/ui.c index 7ac816f20b5..36047803a3c 100644 --- a/dlls/sane.ds/ui.c +++ b/dlls/sane.ds/ui.c @@ -485,20 +485,37 @@ exit: return tpl; }
+/** Data stored for the property sheet dialog while it is open */ +struct SUiData +{ + /** Window handle of the property sheet */ + HWND hwPropertySheet; + + /** Number of property sheet pages */ + int page_count; + + /** Room for property sheet pages */ + PROPSHEETPAGEW psp[20]; +}; + BOOL DoScannerUI(void) { HDC hdc; - PROPSHEETPAGEW psp[10]; + PROPSHEETPAGEW *psp; int page_count= 0; PROPSHEETHEADERW psh; int index = 1; TW_UINT16 rc; int optcount; - UINT psrc; LPWSTR szCaption; DWORD len;
- memset(psp,0,sizeof(psp)); + activeDS.ui_data = (struct SUiData *) calloc(1, sizeof(struct SUiData)); + if (!activeDS.ui_data) + { + return FALSE; + } + psp = activeDS.ui_data->psp; rc = sane_option_get_value( 0, &optcount ); if (rc != TWCC_SUCCESS) { @@ -534,6 +551,7 @@ BOOL DoScannerUI(void)
index ++; } + activeDS.ui_data->page_count = page_count;
len = lstrlenA(activeDS.identity.Manufacturer) + lstrlenA(activeDS.identity.ProductName) + 2; @@ -544,7 +562,7 @@ BOOL DoScannerUI(void) MultiByteToWideChar(CP_ACP,0,activeDS.identity.ProductName,-1, &szCaption[lstrlenA(activeDS.identity.Manufacturer)+1],len); psh.dwSize = sizeof(PROPSHEETHEADERW); - psh.dwFlags = PSH_PROPSHEETPAGE|PSH_PROPTITLE|PSH_USECALLBACK; + psh.dwFlags = PSH_MODELESS|PSH_PROPSHEETPAGE|PSH_PROPTITLE|PSH_USECALLBACK; psh.hwndParent = activeDS.hwndOwner; psh.hInstance = SANE_instance; psh.pszIcon = 0; @@ -554,23 +572,87 @@ BOOL DoScannerUI(void) psh.ppsp = (LPCPROPSHEETPAGEW)psp; psh.pfnCallback = PropSheetProc;
- psrc = PropertySheetW(&psh); + activeDS.ui_data->hwPropertySheet = (HWND) PropertySheetW(&psh);
- for(index = 0; index < page_count; index ++) + if (!activeDS.ui_data->hwPropertySheet) { - free((LPBYTE)psp[index].pResource); - free((LPBYTE)psp[index].pszTitle); + UI_Destroy(); } free(szCaption);
DeleteDC(hdc);
- if (psrc == IDOK) - return TRUE; - else - return FALSE; + return activeDS.ui_data != NULL; +} + + +/** Check if a Message is addressed to the property sheet dialog + */ +BOOL +UI_IsDialogMessage(MSG *msg) +{ + return + activeDS.ui_data && + activeDS.ui_data->hwPropertySheet && + SendMessageW(activeDS.ui_data->hwPropertySheet, PSM_ISDIALOGMESSAGE, 0, (LPARAM) msg); +} + + +/** Destroy the property sheet dialog and associated structures + */ +void +UI_Destroy(void) +{ + if (activeDS.ui_data) + { + if(activeDS.ui_data->hwPropertySheet) + { + DestroyWindow(activeDS.ui_data->hwPropertySheet); + } + for(int index = 0; index < activeDS.ui_data->page_count; index ++) + { + free((LPBYTE)activeDS.ui_data->psp[index].pResource); + free((LPBYTE)activeDS.ui_data->psp[index].pszTitle); + } + free(activeDS.ui_data); + activeDS.ui_data = NULL; + } + if (activeDS.ModalUI) + { + EnableWindow(activeDS.hwndOwner, TRUE); + } +} + +/** + * @brief control enable state of scan dialog + * When finished scanning, re-enable the UI Dialog. + * + * @param enable TRUE to enable, FALSE to disable + */ +void +UI_Enable(BOOL enable) +{ + HWND hwndControl; + + if (activeDS.ui_data && + activeDS.ui_data->hwPropertySheet) + { + EnableWindow(activeDS.ui_data->hwPropertySheet, enable); + + // Give the user a bit of optical feedback + if (NULL != (hwndControl=GetDlgItem(activeDS.ui_data->hwPropertySheet, IDOK))) + { + EnableWindow(hwndControl, enable); + } + if (NULL != (hwndControl=GetDlgItem(activeDS.ui_data->hwPropertySheet, IDCANCEL))) + { + EnableWindow(hwndControl, enable); + } + } }
+ + static BOOL save_to_reg( DWORD reg_type, CHAR* name, const BYTE* value, DWORD size ) { HKEY h_key; @@ -1020,7 +1102,12 @@ static INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM case PSN_APPLY: if (psn->lParam) { - SANE_XferReady(); + if (IsWindowEnabled(activeDS.ui_data->hwPropertySheet)) + { + SANE_XferReady(); + /* Disable the DS UI while scanning */ + UI_Enable(FALSE); + } } break; case PSN_QUERYCANCEL: @@ -1105,7 +1192,30 @@ HWND ScanningDialogBox(HWND dialog, LONG progress)
if (!dialog) { - dialog = CreateDialogW(SANE_instance, MAKEINTRESOURCEW(IDD_SCANNING), NULL, ScanningProc); + HWND hwndOwner= + activeDS.ui_data + ? activeDS.ui_data->hwPropertySheet + : NULL; + dialog = CreateDialogW(SANE_instance, MAKEINTRESOURCEW(IDD_SCANNING), hwndOwner, ScanningProc); + + if (dialog) + { + if (hwndOwner) + { + RECT rcDialog, rcOwner; + GetWindowRect(dialog, &rcDialog); + GetWindowRect(hwndOwner, &rcOwner); + SetWindowPos(dialog, NULL, + (rcOwner.right+rcOwner.left)/2 - (rcDialog.right-rcDialog.left)/2, + (rcOwner.bottom+rcOwner.top)/2 - (rcDialog.bottom-rcDialog.top)/2, + 0, 0, + SWP_NOSIZE|SWP_SHOWWINDOW|SWP_NOZORDER); + } + else + { + ShowWindow(dialog, SW_SHOW); + } + } }
if (progress == -1)