From a14e14c641aa7ea080ecf67d230d86f18eb05779 Mon Sep 17 00:00:00 2001 From: Dario Sassi Date: Tue, 7 Jul 2026 09:06:59 +0200 Subject: [PATCH] =?UTF-8?q?EgtExecutor=203.1g2=20:=20-=20sistemazioni=20pe?= =?UTF-8?q?r=20selezione=20entit=C3=A0=20in=20dialogo=20lua.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AuxDialogBox.cpp | 91 ----- AuxDialogBox.h | 31 -- EXE_GdbObjSelection.cpp | 32 +- EgtExecutor.rc | Bin 20614 -> 20614 bytes EgtExecutor.vcxproj | 186 ++++++++- EgtExecutor.vcxproj.filters | 552 ++++++++++++++++++++++++- GenTools.cpp | 56 --- GenTools.h | 22 - LUA_GenDialog.cpp | 780 ++++++++++++++++++++++++++++++++++++ LUA_GenDialog.h | 24 ++ LUA_General.cpp | 702 +------------------------------- stdafx.h | 1 + 12 files changed, 1552 insertions(+), 925 deletions(-) delete mode 100644 AuxDialogBox.cpp delete mode 100644 AuxDialogBox.h delete mode 100644 GenTools.cpp delete mode 100644 GenTools.h create mode 100644 LUA_GenDialog.cpp create mode 100644 LUA_GenDialog.h diff --git a/AuxDialogBox.cpp b/AuxDialogBox.cpp deleted file mode 100644 index 072200e..0000000 --- a/AuxDialogBox.cpp +++ /dev/null @@ -1,91 +0,0 @@ -//---------------------------------------------------------------------------- -// EgalTech 2026-2026 -//---------------------------------------------------------------------------- -// File : AuxDialogBox.cpp Data : 24.04.26 Versione : 3.1d4 -// Contenuto : Funzioni DialogBox. -// -// -// Modifiche : 24.04.26 RE Creazione modulo. -// -// -//---------------------------------------------------------------------------- - -//--------------------------- Include ---------------------------------------- -#include "stdafx.h" -#include "EXE.h" -#include "EXE_Macro.h" -#include "AuxDialogBox.h" -#include "/EgtDev/Include/EGnStringUtils.h" -#include "/EgtDev/Include/EXeExecutor.h" -#include "/EgtDev/Include/EGkStringUtils3d.h" -#include "/EgtDev/Include/EgtStringConverter.h" - -using namespace std ; - -#pragma comment( lib, "Comctl32.lib") // per funzioni LRESULT CALLBACK - -HWND phDlgModeless = nullptr ; -int nDlgModelessItem = -1 ; - -//----------------------------------------------------------------------------- -bool -UpdateIdsModelessDialog( HWND phDlgModeless, int nDlgModelessItem) -{ - // se DialogBox Modeless non inizializzato, esco - if ( phDlgModeless == nullptr) - return false ; - // se Id del controllo da aggiornare non valido, esco - if ( nDlgModelessItem == -1) - return false ; - - // recupero tutti gli elementi selezionati - string sIds ; - int nId = ExeGetFirstSelectedObj() ; - while ( nId != GDB_ID_NULL) { - sIds.append( ToString( nId) + string{","}) ; - nId = ExeGetNextSelectedObj() ; - } - if ( ! sIds.empty()) - sIds.pop_back() ; - - // aggiorno il contenuto nel dialogo - return ( SetDlgItemText( phDlgModeless, nDlgModelessItem, stringtoW( sIds))) ; -} - -//----------------------------------------------------------------------------- -LRESULT -CALLBACK UpdateSelectionModelessDialog( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) -{ - if ( msg == WM_GETDLGCODE) - return DLGC_WANTALLKEYS ; - - if ( msg == WM_KEYDOWN && wParam == VK_RETURN) { - // dwRefData contiene l'ID dell'EDIT - int nEditId = int( dwRefData) ; - HWND hwndDlg = GetParent(hwnd) ; - if ( hwndDlg == nullptr) - return FALSE ; - // recupero il testo dell'EDIT corretto - string sIds ; - AtoWEX<128> wsEdit( "") ; - if ( GetDlgItemText( hwndDlg, nEditId, LPWSTR( wsEdit), 128) > 0) - sIds = wstrztoA( wsEdit) ; - // deseleziono tutto - ExeDeselectAll() ; - // seleziono gli ID contenuti - if ( ! sIds.empty()) { - STRVECTOR vsIds; - Tokenize( sIds, ",", vsIds) ; - for ( const string& s : vsIds) { - int nId = GDB_ID_NULL ; - if ( FromString( s, nId) && nId != GDB_ID_NULL) - ExeSelectObj( nId) ; - } - } - // aggiorno la grafica ed esco - ExeDraw() ; - return TRUE ; - } - - return ( DefSubclassProc( hwnd, msg, wParam, lParam)) ; -} diff --git a/AuxDialogBox.h b/AuxDialogBox.h deleted file mode 100644 index 25466fc..0000000 --- a/AuxDialogBox.h +++ /dev/null @@ -1,31 +0,0 @@ -//---------------------------------------------------------------------------- -// EgalTech 2026-2026 -//---------------------------------------------------------------------------- -// File : AuxDialogBox.h Data : 24.04.2026 Versione : 3.1d4 -// Contenuto : Prototipi funzioni DialogBox. -// -// -// -// Modifiche : 22.04.26 RE Creazione modulo. -// -// -//---------------------------------------------------------------------------- - -#pragma once - -#include "stdafx.h" -#include "resource.h" -#include "/EgtDev/Include/ExeExecutor.h" -#include "/EgtDev/Include/EGkLuaAux.h" -#include - -//---------------------------------------------------------------------------- -// Identificativo della finestra ModeLess corrente ( nullptr se non esiste) -extern HWND phDlgModeless ; -extern int nDlgModelessItem ; - -// Funzione per passaggi di parametri tra Selezione DB e dialogo non modale -bool UpdateIdsModelessDialog( HWND phDlgModeless, int UpdateIdsModelessDialog) ; - -//Funzione per selezionare gli Id presenti all'interno dei un EditText -LRESULT CALLBACK UpdateSelectionModelessDialog( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) ; diff --git a/EXE_GdbObjSelection.cpp b/EXE_GdbObjSelection.cpp index cd7c3af..1e96ac1 100644 --- a/EXE_GdbObjSelection.cpp +++ b/EXE_GdbObjSelection.cpp @@ -15,7 +15,7 @@ #include "stdafx.h" #include "EXE.h" #include "EXE_Macro.h" -#include "AuxDialogBox.h" +#include "LUA_GenDialog.h" #include "/EgtDev/Include/EXeExecutor.h" #include "/EgtDev/Include/EGkStringUtils3d.h" #include "/EgtDev/Include/EgtStringConverter.h" @@ -71,15 +71,15 @@ ExeSelectObj( int nId) bOk = pGeomDB->SelectObj( nId) ; else bOk = pGeomDB->ExistsObj( nId) ; + // eventuale aggiornamento dialogo modeless con selezione attiva + if ( bOk) + UpdateDialogIds() ; // se richiesto, salvo il comando Lua equivalente if ( IsCmdLog()) { string sLua = "EgtSelectObj(" + ToString( nId) + ")" + " -- Ok=" + ToString( bOk) ; LOG_INFO( GetCmdLogger(), sLua.c_str()) ; } - // verifico se presente un dialogo modeless per aggiornare i valori - if ( bOk && phDlgModeless != nullptr && nDlgModelessItem != -1) - UpdateIdsModelessDialog( phDlgModeless, nDlgModelessItem) ; // restituisco risultato return bOk ; } @@ -98,15 +98,15 @@ ExeDeselectObj( int nId) pGseCtx->m_siSel.ResetLast() ; else if ( nId == pGseCtx->m_siSel.nPrevId) pGseCtx->m_siSel.ResetPrev() ; + // eventuale aggiornamento dialogo modeless con selezione attiva + if ( bOk) + UpdateDialogIds() ; // se richiesto, salvo il comando Lua equivalente if ( IsCmdLog()) { string sLua = "EgtDeselectObj(" + ToString( nId) + ")" + " -- Ok=" + ToString( bOk) ; LOG_INFO( GetCmdLogger(), sLua.c_str()) ; } - // verifico se presente un dialogo modeless per aggiornare i valori - if ( bOk && phDlgModeless != nullptr && nDlgModelessItem != -1) - UpdateIdsModelessDialog( phDlgModeless, nDlgModelessItem) ; // restituisco risultato return bOk ; } @@ -161,15 +161,15 @@ ExeDeselectAll( void) bool bOk = pGeomDB->ClearSelection() ; // reset info ultimi due selezionati pGseCtx->m_siSel.Reset() ; + // eventuale aggiornamento dialogo modeless con selezione attiva + if ( bOk) + UpdateDialogIds() ; // se richiesto, salvo il comando Lua equivalente if ( IsCmdLog()) { string sLua = "EgtDeselectAll()" " -- Ok=" + ToString( bOk) ; LOG_INFO( GetCmdLogger(), sLua.c_str()) ; } - // verifico se presente un dialogo modeless per aggiornare i valori - if ( bOk && phDlgModeless != nullptr && nDlgModelessItem != -1) - UpdateIdsModelessDialog( phDlgModeless, nDlgModelessItem) ; // restituisco risultato return bOk ; } @@ -190,15 +190,15 @@ ExeSelectGroupObjs( int nGroupId) } else bOk = false ; + // eventuale aggiornamento dialogo modeless con selezione attiva + if ( bOk) + UpdateDialogIds() ; // se richiesto, salvo il comando Lua equivalente if ( IsCmdLog()) { string sLua = "EgtSelectGroupObjs(" + ToString( nGroupId) + ")" + " -- Ok=" + ToString( bOk) ; LOG_INFO( GetCmdLogger(), sLua.c_str()) ; } - // verifico se presente un dialogo modeless per aggiornare i valori - if ( bOk && phDlgModeless != nullptr && nDlgModelessItem != -1) - UpdateIdsModelessDialog( phDlgModeless, nDlgModelessItem) ; // restituisco risultato return bOk ; } @@ -216,15 +216,15 @@ ExeDeselectGroupObjs( int nGroupId) } else bOk = false ; + // eventuale aggiornamento dialogo modeless con selezione attiva + if ( bOk) + UpdateDialogIds() ; // se richiesto, salvo il comando Lua equivalente if ( IsCmdLog()) { string sLua = "EgtDeselectGroupObjs(" + ToString( nGroupId) + ")" + " -- Ok=" + ToString( bOk) ; LOG_INFO( GetCmdLogger(), sLua.c_str()) ; } - // verifico se presente un dialogo modeless per aggiornare i valori - if ( bOk && phDlgModeless != nullptr && nDlgModelessItem != -1) - UpdateIdsModelessDialog( phDlgModeless, nDlgModelessItem) ; // restituisco risultato return bOk ; } diff --git a/EgtExecutor.rc b/EgtExecutor.rc index 9bda0fac528ecafc55b33fb71bc16b0ddd491f2b..121c0d0b6516b8f42f812d76502b9bce118af544 100644 GIT binary patch delta 81 zcmZo$$k?`!al_X*j6L{E!>)d)%< O)HEo<)Wj)i=m7wCaT>P( delta 81 zcmZo$$k?`!al_X*j6L{E!>)d)%< O)HEo<)Wj)i=m7wBB^s{) diff --git a/EgtExecutor.vcxproj b/EgtExecutor.vcxproj index afca94f..7528b45 100644 --- a/EgtExecutor.vcxproj +++ b/EgtExecutor.vcxproj @@ -222,13 +222,194 @@ copy $(TargetPath) \EgtProg\Dll64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -250,7 +431,6 @@ copy $(TargetPath) \EgtProg\Dll64 - @@ -307,7 +487,6 @@ copy $(TargetPath) \EgtProg\Dll64 - @@ -319,6 +498,7 @@ copy $(TargetPath) \EgtProg\Dll64 + diff --git a/EgtExecutor.vcxproj.filters b/EgtExecutor.vcxproj.filters index d88ca92..69f7156 100644 --- a/EgtExecutor.vcxproj.filters +++ b/EgtExecutor.vcxproj.filters @@ -111,9 +111,552 @@ File di intestazione - + File di intestazione + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + + + File di intestazione\Include + @@ -305,9 +848,6 @@ File di origine\LUA - - File di origine\Global - File di origine\EXE @@ -434,8 +974,8 @@ File di origine\LUA - - File di origine\Global + + File di origine\LUA diff --git a/GenTools.cpp b/GenTools.cpp deleted file mode 100644 index 91fbf7f..0000000 --- a/GenTools.cpp +++ /dev/null @@ -1,56 +0,0 @@ -//---------------------------------------------------------------------------- -// EgalTech 2017-2017 -//---------------------------------------------------------------------------- -// File : GenTools.cpp Data : 08.01.17 Versione : 1.6x5 -// Contenuto : Funzioni generalie. -// -// -// -// Modifiche : 08.01.17 DS Creazione modulo. -// -// -//---------------------------------------------------------------------------- - -#include "stdafx.h" -#include "GenTools.h" -#include - -using namespace std ; - -//---------------------------------------------------------------------------- -HWND -FindTopWindow( void) -{ - // Recupero l'Id del processo corrente - DWORD pid = GetCurrentProcessId() ; - - pair params = { 0, pid} ; - - // Enumerate the windows using a lambda to process each window - SetLastError( 0) ; - BOOL bResult = EnumWindows( []( HWND hWnd, LPARAM lParam) -> BOOL - { - auto pParams = ( pair*)(lParam) ; - - DWORD processId ; - if ( GetWindowThreadProcessId( hWnd, &processId) != NULL && - processId == pParams->second && - GetWindow( hWnd, GW_OWNER) == NULL && - IsWindowVisible( hWnd)) { - // Stop enumerating - SetLastError( -1) ; - pParams->first = hWnd ; - return FALSE ; - } - - // Continue enumerating - return TRUE ; - }, (LPARAM) ¶ms) ; - - - if ( ! bResult && GetLastError() == -1 && params.first != nullptr) { - return params.first ; - } - - return nullptr ; -} \ No newline at end of file diff --git a/GenTools.h b/GenTools.h deleted file mode 100644 index 851371d..0000000 --- a/GenTools.h +++ /dev/null @@ -1,22 +0,0 @@ -//---------------------------------------------------------------------------- -// EgalTech 2017-2017 -//---------------------------------------------------------------------------- -// File : GenTools.h Data : 08.01.17 Versione : 1.6x5 -// Contenuto : Prototipi funzioni generiche. -// -// -// -// Modifiche : 08.01.17 DS Creazione modulo. -// -// -//---------------------------------------------------------------------------- - -#pragma once - -#define NOMINMAX -#include - -//---------------------------------------------------------------------------- -// Recupero l'handle della finestra principale dell'eseguibile -HWND FindTopWindow( void) ; - diff --git a/LUA_GenDialog.cpp b/LUA_GenDialog.cpp new file mode 100644 index 0000000..c7cd270 --- /dev/null +++ b/LUA_GenDialog.cpp @@ -0,0 +1,780 @@ +//---------------------------------------------------------------------------- +// EgalTech 2026-2026 +//---------------------------------------------------------------------------- +// File : LUA_GenDialog.cpp Data : 06.07.26 Versione : 3.1g2 +// Contenuto : Funzioni gestione dialoghi per LUA. +// +// +// +// Modifiche : 06.07.26 DS Creazione modulo. +// +// +//---------------------------------------------------------------------------- + +//--------------------------- Include ---------------------------------------- +#include "stdafx.h" +#include "DllMain.h" +#include "EXE.h" +#include "LUA.h" +#include "LUA_GenDialog.h" +#include "resource.h" +#include "/EgtDev/Include/ExeExecutor.h" +#include "/EgtDev/Include/EGkLuaAux.h" +#include "/EgtDev/Include/EGkStringUtils3d.h" +#include "/EgtDev/Include/EgtIniFile.h" +#include "/EgtDev/Include/EgtNumUtils.h" +#include +#include + +using namespace std ; + +//---------------------------------------------------------------------------- +// Costanti +const int MAX_CTRLS = IDC_TEXT8 - IDC_TEXT1 + 1 ; +const int OFFS_CTRLS = 40 ; +enum TYPE_CTRLS { CTRL_NONE = 0, CTRL_CHECK = 1, CTRL_COMBO = 2, CTRL_EDIT = 3, CTRL_COLOR = 4, CTRL_BUTTON = 5} ; +const char* SEC_SCENE = "Scene" ; +const char* KEY_CUSTOMCOLORS = "CustomColors" ; +// Tipi +struct DialogContext { + bool bDone = false ; // flag per chiusura dialogo (X/Ok/Cancel) + bool bCancelled = false ; // true se X/Cancel + STRVECTOR vsResult ; // valori finali da ritornare a Lua +} ; +// Variabili Globali +static COLORREF s_CustomColors[16] = {12632256} ; // 16 colori GRAY +static bool s_bCustomColorsInit = false ; // flag di inizializzazione colori +static HWND s_hDlg = NULL ; +static string s_sCaption ; +static int s_nCtrls = 0 ; +static string s_sText[MAX_CTRLS] ; +static string s_sEdit[MAX_CTRLS] ; +static int s_nType[MAX_CTRLS] ; +static bool s_bSelectionMode = false ; // se true allora diventa dialogo "Modeless" +static int s_nActiveRow = - 1 ; // indice della riga di "SB:" attiva +static int s_nDlgItem = -1 ; // indice dell'Item di selezione + +//------------------------------------------------------------------------------- +// OnClosingDialog +// +// Scopo: +// Gestisce la chiusura del dialogo modeless in modo centralizzato e sicuro. +// Questa funzione viene chiamata quando l’utente chiude il dialogo tramite: +// - OK +// - Cancel +// - pulsante X +// - WM_CLOSE +// +// Comportamento: +// - Riabilita la finestra principale (Main Window), che era stata disabilitata durante il funzionamento +// modale del dialogo. +// - Distrugge la finestra del dialogo. +// - Riporta il focus alla finestra principale, garantendo continuità nell’interazione dell’utente. +// - Deseleziona tutti gli oggetti nella scena e aggiorna la vista, in modo che nessuna selezione residua +// rimanga attiva dopo la chiusura. +//------------------------------------------------------------------------------- +static void +OnClosingDialog( HWND hwndDlg) +{ + // reset stato globale + s_bSelectionMode = false ; + s_nActiveRow = -1 ; + s_nDlgItem = -1 ; + // il controllo passa al Main + HWND hMain = GetParent( hwndDlg) ; + EnableWindow( hMain, TRUE) ; + DestroyWindow( hwndDlg) ; + s_hDlg = NULL ; + SetFocus( hMain) ; + // deseleziono tutto + ExeDeselectAll() ; + ExeDraw() ; +} + +//------------------------------------------------------------------------------- +// StartSelectionMode +// +// Scopo: +// Attiva la modalità di selezione (modeless) del dialogo. Questa modalità permette all’utente di interagire +// con la scena mentre il dialogo rimane aperto e attivo. +// +// Parametri: +// hDlg → handle del dialogo +// nEditId → ID dell’edit associato al pulsante BS premuto +// nActiveRow → indice della riga BS attiva (0..MAX_CTRLS-1) +//------------------------------------------------------------------------------- +static void +StartSelectionMode( HWND hDlg, int nEditId, int nActiveRow) +{ + // il dialogo torna modeless rispetto alla scena + HWND hMain = GetParent( hDlg) ; + EnableWindow( hMain, TRUE) ; + // resta comunque in primo piano + SetWindowPos( hDlg, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE) ; + + // disabilito tutti gli edit associati agli SB tranne quello attivo + for ( int i = 0; i < s_nCtrls ; ++ i) { + if ( s_sEdit[i].find( "SB:") == 0) { + int idEdit = IDC_EDIT1 + i ; + EnableWindow( GetDlgItem( hDlg, idEdit), ( i == nActiveRow ? TRUE : FALSE)) ; + } + } + + // seleziono solo gli elementi presenti nell'edit corrispondente + s_nDlgItem = - 1 ; + ExeDeselectAll() ; // !<-- NB. avendo messo s_nDlgItem = -1 il testo non viene rimosso + s_bSelectionMode = true ; + s_nActiveRow = nActiveRow ; + s_nDlgItem = nEditId ; + AtoWEX<128> wsEdit( "") ; + GetDlgItemText( hDlg, nEditId, LPWSTR( wsEdit), 128) ; + string sIds = wstrztoA( wsEdit) ; + STRVECTOR vsIds ; + Tokenize( sIds, ",", vsIds) ; + for ( const string& sId : vsIds) { + int nId = GDB_ID_NULL ; + if ( FromString( sId, nId) && nId != GDB_ID_NULL) + ExeSelectObj( nId) ; + } + ExeDraw() ; +} + +//------------------------------------------------------------------------------- +// EndSelectionMode +// +// Scopo: +// Termina la modalità di selezione e riporta il dialogo in stato modale. +//------------------------------------------------------------------------------- +static void +EndSelectionMode( HWND hDlg) +{ + s_bSelectionMode = false ; + s_nActiveRow = -1 ; + s_nDlgItem = -1 ; + + // disabilito tutti gli edit associati ai BS + for ( int i = 0; i < s_nCtrls ; ++ i) { + if ( s_sEdit[i].find( "SB:") == 0) { + int idEdit = IDC_EDIT1 + i ; + EnableWindow( GetDlgItem( hDlg, idEdit), FALSE) ; + SetDlgItemText( hDlg, IDC_BUTTON_SEL1 + i, strztoW( "+")) ; + } + } + + // il dialogo torna modale + HWND hMain = GetParent( hDlg) ; + EnableWindow( hMain, FALSE) ; + SetFocus( hDlg) ; + // deseleziono tutto + ExeDeselectAll() ; //!<-- NB. avendo messo nDlgModelessItem = -1 il testo non viene rimosso + ExeDraw() ; +} + +//----------------------------------------------------------------------------- +LRESULT +CALLBACK UpdateDialogSelection( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) +{ + if ( msg == WM_GETDLGCODE) + return DLGC_WANTALLKEYS ; + + if ( msg == WM_KEYDOWN && wParam == VK_RETURN) { + // dwRefData contiene l'ID dell'EDIT + int nEditId = int( dwRefData) ; + HWND hwndDlg = GetParent(hwnd) ; + if ( hwndDlg == nullptr) + return FALSE ; + // recupero il testo dell'EDIT corretto + string sIds ; + AtoWEX<128> wsEdit( "") ; + if ( GetDlgItemText( hwndDlg, nEditId, LPWSTR( wsEdit), 128) > 0) + sIds = wstrztoA( wsEdit) ; + // deseleziono tutto + ExeDeselectAll() ; + // seleziono gli ID contenuti + if ( ! sIds.empty()) { + STRVECTOR vsIds; + Tokenize( sIds, ",", vsIds) ; + for ( const string& s : vsIds) { + int nId = GDB_ID_NULL ; + if ( FromString( s, nId) && nId != GDB_ID_NULL) + ExeSelectObj( nId) ; + } + } + // aggiorno la grafica ed esco + ExeDraw() ; + return TRUE ; + } + + return ( DefSubclassProc( hwnd, msg, wParam, lParam)) ; +} + +//------------------------------------------------------------------------------- +BOOL +CALLBACK DialogBoxProc( HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + // variabili static locali per mantenere lo stato tra chiamate per selezione colori + static COLORREF selectedColor[MAX_CTRLS] = {0} ; // BLACK + static HBRUSH hColorBrush[MAX_CTRLS] = {NULL} ; // non definito + + switch ( message) { + case WM_INITDIALOG : + { + // imposto il dialogo come modale + HWND hMain = GetParent( hwndDlg) ; + EnableWindow( hMain, FALSE) ; + SetFocus( hwndDlg) ; + + // imposto il contesto della funzione per recuperalo in futuro (per la funzione di CallBack Lua) + SetWindowLongPtr( hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam) ; + + // colore di default + COLORREF defaultColor = RGB( 255, 0, 0) ; + // inizializzazione colori e brush + for ( int i = 0 ; i < MAX_CTRLS ; ++ i) { + selectedColor[i] = defaultColor ; + if ( hColorBrush[i] != NULL) + DeleteObject( hColorBrush[i]) ; + hColorBrush[i] = CreateSolidBrush( selectedColor[i]) ; + } + // dati del dialogo + HWND hwndOwner = GetParent( hwndDlg) ; + if ( hwndOwner == nullptr) + hwndOwner = GetDesktopWindow() ; + RECT rcOwner, rcDlg ; + GetWindowRect( hwndOwner, &rcOwner) ; + GetWindowRect( hwndDlg, &rcDlg) ; + // determino la posizione dell'ultima riga di dati da visualizzare + HWND hwndLR = GetDlgItem( hwndDlg, IDC_TEXT1 + Clamp( s_nCtrls, 1, MAX_CTRLS) - 1) ; + RECT rcLR ; + GetWindowRect( hwndLR, &rcLR) ; + POINT ptLR{ rcLR.left, rcLR.top} ; + ScreenToClient( hwndDlg, &ptLR) ; + // centro e scalo il dialogo + int nNewH = rcLR.top - rcOwner.top + 2 * OFFS_CTRLS ; // non chiaro perchè va sottratto rcOwner.top ma funziona sempre + SetWindowPos( hwndDlg, HWND_TOP, + ( rcOwner.left + rcOwner.right - ( rcDlg.right - rcDlg.left)) / 2, + ( rcOwner.top + rcOwner.bottom - nNewH) / 2, + ( rcDlg.right - rcDlg.left), + nNewH, 0) ; + // riposiziono il bottone Ok + HWND hwndOk = GetDlgItem( hwndDlg, IDOK) ; + RECT rcOk ; + GetWindowRect( hwndOk, &rcOk) ; + POINT ptOk{ rcOk.left, rcOk.top} ; + ScreenToClient( hwndDlg, &ptOk) ; + SetWindowPos( hwndOk, hwndDlg, ptOk.x, ptLR.y + OFFS_CTRLS, 0, 0, SWP_NOSIZE | SWP_NOZORDER) ; + // riposiziono il bottone Cancel + HWND hwndCl = GetDlgItem( hwndDlg, IDCANCEL) ; + RECT rcCl ; + GetWindowRect( hwndCl, &rcCl) ; + POINT ptCl{ rcCl.left, rcCl.top} ; + ScreenToClient( hwndDlg, &ptCl) ; + SetWindowPos( hwndCl, hwndDlg, ptCl.x, ptLR.y + OFFS_CTRLS, 0, 0, SWP_NOSIZE | SWP_NOZORDER) ; + // stile finestra + LONG style = GetWindowLong( hwndDlg, GWL_STYLE) ; + style |= WS_SYSMENU ; + SetWindowLong( hwndDlg, GWL_STYLE, style) ; + } + + // assegno titolo del dialogo + SetWindowText( hwndDlg, stringtoW( s_sCaption)) ; + + // assegno testi e valori di default e nascondo linee dei controlli non usati + for ( int i = 0 ; i < MAX_CTRLS ; ++ i) { + if ( i < s_nCtrls) { + + // imposto il testo nella riga corrente + SetDlgItemText( hwndDlg, IDC_TEXT1 + i, stringtoW( s_sText[i])) ; + + // --- se CheckBox + if ( s_sEdit[i].find( "CK:") == 0) { + bool bChecked = false ; + FromString( s_sEdit[i].substr( 3), bChecked) ; + CheckDlgButton( hwndDlg, IDC_CHECK1 + i, ( bChecked ? BST_CHECKED : BST_UNCHECKED)) ; + ShowWindow( GetDlgItem( hwndDlg, IDC_EDIT1 + i), SW_HIDE) ; // nascondo Edit + ShowWindow( GetDlgItem( hwndDlg, IDC_COMBO1 + i), SW_HIDE) ; // nascondo Combo + ShowWindow( GetDlgItem( hwndDlg, IDC_BUTTON_SEL1 + i), SW_HIDE) ; // nascondo Button di selezione + s_nType[i] = CTRL_CHECK ; + } + + // --- se ComboBox + else if ( s_sEdit[i].find( "CB:") == 0) { + string sList = s_sEdit[i].substr( 3) ; + STRVECTOR vsVal ; + Tokenize( sList, ",", vsVal) ; + int nSel = 0 ; + HWND hwndCBox = GetDlgItem( hwndDlg, IDC_COMBO1 + i) ; + for ( int j = 0 ; j < ssize( vsVal) ; ++ j) { + string sItem ; + if ( vsVal[j][0] == '*') { + nSel = j ; + sItem = vsVal[j].substr( 1) ; + } + else + sItem = vsVal[j] ; + ComboBox_AddString( hwndCBox, stringtoW( sItem)) ; + } + ComboBox_SetCurSel( hwndCBox, nSel) ; + int nEditHeight = int( SendMessage( hwndCBox, CB_GETITEMHEIGHT, (WPARAM)-1, 0)) ; + int nItemHeight = int( SendMessage( hwndCBox, CB_GETITEMHEIGHT, 0, 0)) ; + int nTotalHeight = nEditHeight + nItemHeight * ( ssize( vsVal) + 1) ; + RECT rc ; GetWindowRect( hwndCBox, &rc) ; + HWND parent = GetParent(hwndCBox) ; MapWindowPoints( NULL, parent, (LPPOINT)&rc, 2) ; + SetWindowPos( hwndCBox, NULL, rc.left, rc.top, rc.right - rc.left, nTotalHeight, SWP_NOZORDER) ; + ShowWindow( GetDlgItem( hwndDlg, IDC_EDIT1 + i), SW_HIDE) ; // nascondo Edit + ShowWindow( GetDlgItem( hwndDlg, IDC_CHECK1 + i), SW_HIDE) ; // nascondo CheckBox + ShowWindow( GetDlgItem( hwndDlg, IDC_BUTTON_SEL1 + i), SW_HIDE) ; // nascondo Button di selezione + s_nType[i] = CTRL_COMBO ; + } + + // --- se ColorPicker + else if ( s_sEdit[i].find( "CP:") == 0) { + string sVal = s_sEdit[i].substr( 3) ; + Color CColor ; + if ( ! FromString( sVal, CColor)) + CColor = INVISIBLE ; + selectedColor[i] = RGB( CColor.GetIntRed(), CColor.GetIntGreen(), CColor.GetIntBlue()) ; + if ( hColorBrush[i] != NULL) + DeleteObject( hColorBrush[i]) ; + hColorBrush[i] = CreateSolidBrush( selectedColor[i]) ; + // recupero i valori dei colori personalizzati dal file .ini + if ( ! s_bCustomColorsInit) { + // recupero il file Ini + string sIniFile = ExeGetIniFile() ; + if ( ! sIniFile.empty()) { + string sCustomVals = GetPrivateProfileStringUtf8( SEC_SCENE, KEY_CUSTOMCOLORS, "", sIniFile.c_str()) ; + STRVECTOR vsWinColors ; Tokenize( sCustomVals, ",", vsWinColors) ; + for ( int j = 0 ; j < ssize( vsWinColors) && j < 16 ; ++ j) { + unsigned int unVal ; + if ( FromString( vsWinColors[j], unVal)) { + unsigned int unR = unVal & 0xFF ; + unsigned int unG = ( unVal >> 8) & 0xFF ; + unsigned int unB = ( unVal >> 16) & 0xFF ; + s_CustomColors[j] = RGB( unR, unG, unB) ; + } + } + } + s_bCustomColorsInit = true ; + } + // nascondo qualsiasi tipo di componente + ShowWindow( GetDlgItem( hwndDlg, IDC_EDIT1 + i), SW_HIDE) ; // nascondo Edit + ShowWindow( GetDlgItem( hwndDlg, IDC_COMBO1 + i), SW_HIDE) ; // nascondo Combo + ShowWindow( GetDlgItem( hwndDlg, IDC_CHECK1 + i), SW_HIDE) ; // nascondo Check + ShowWindow( GetDlgItem( hwndDlg, IDC_BUTTON_SEL1 + i), SW_HIDE) ; // nascondo Button di selezione + + // margini per dimensione del rettangolo preview pickColor + const int PREVIEW_HEIGHT = 20 ; // altezza del rettangolo + const int PREVIEW_MIN_WIDTH = 40 ; // larghezza minima + + // creazione della static preview + HWND hwndPreview = NULL ; + HWND hwndEdit = GetDlgItem( hwndDlg, IDC_EDIT1 + i) ; + RECT rcEdit ; GetClientRect( hwndDlg, &rcEdit) ; // coordinate client del dialog + if ( hwndEdit != nullptr && GetWindowRect( hwndEdit, &rcEdit)) { + // converti rcLabel in coordinate client + POINT ptEdit = { rcEdit.left, rcEdit.top } ; + ScreenToClient( hwndDlg, &ptEdit) ; + int x = ptEdit.x ; + int y = ptEdit.y ; + int width = rcEdit.right - rcEdit.left ; + if ( width < PREVIEW_MIN_WIDTH) + width = PREVIEW_MIN_WIDTH ; + hwndPreview = CreateWindowEx( 0, L"STATIC", NULL, + WS_CHILD | WS_VISIBLE | SS_NOTIFY | SS_CENTERIMAGE | WS_BORDER, + x, y, width, PREVIEW_HEIGHT, + hwndDlg, ( HMENU)INT_PTR( IDC_COLOR1 + i), + GetModuleHandle( NULL), NULL) ; + } + // pulizia elementi testuali + if ( hwndPreview) { + SetWindowText( hwndPreview, L"") ; + SetWindowPos( hwndPreview, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE) ; + InvalidateRect( hwndPreview, NULL, TRUE) ; + UpdateWindow( hwndPreview) ; + } + s_nType[i] = CTRL_COLOR ; + } + + // --- se Button di selezione + else if ( s_sEdit[i].find( "SB:") == 0) { + ShowWindow( GetDlgItem( hwndDlg, IDC_COMBO1 + i), SW_HIDE) ; // nascondo la Combo + ShowWindow( GetDlgItem( hwndDlg, IDC_CHECK1 + i), SW_HIDE) ; // nascondo la Check + SetDlgItemText( hwndDlg, IDC_BUTTON_SEL1 + i, strztoW( "+")) ; // modalità non Selezione + EnableWindow( GetDlgItem( hwndDlg, IDC_EDIT1 + i), FALSE) ; // disattivo la Edit + // associo alla casella di testo la funzione di KeyPress su Enter + HWND hEdit = GetDlgItem( hwndDlg, IDC_EDIT1 + i) ; + if ( hEdit != nullptr) + SetWindowSubclass( hEdit, UpdateDialogSelection, 1, (DWORD_PTR)( IDC_EDIT1 + i)) ; + s_nType[i] = CTRL_BUTTON ; + } + + // --- se Testo + else { + SetDlgItemText( hwndDlg, IDC_EDIT1 + i, stringtoW( s_sEdit[i])) ; + ShowWindow( GetDlgItem( hwndDlg, IDC_COMBO1 + i), SW_HIDE) ; // nascondo Combo + ShowWindow( GetDlgItem( hwndDlg, IDC_CHECK1 + i), SW_HIDE) ; // nascondo Check + ShowWindow( GetDlgItem( hwndDlg, IDC_BUTTON_SEL1 + i), SW_HIDE) ; // nascondo Button di selezione + s_nType[i] = CTRL_EDIT ; + } + } + // se numero di controllo superiore al massimo, non visualizzo nullo ( in questo caso aggiungere elementi a Dialog in .rc) + else { + // nascondo tutto + ShowWindow( GetDlgItem( hwndDlg, IDC_TEXT1 + i), SW_HIDE) ; + ShowWindow( GetDlgItem( hwndDlg, IDC_EDIT1 + i), SW_HIDE) ; + ShowWindow( GetDlgItem( hwndDlg, IDC_COMBO1 + i), SW_HIDE) ; + ShowWindow( GetDlgItem( hwndDlg, IDC_CHECK1 + i), SW_HIDE) ; + ShowWindow( GetDlgItem( hwndDlg, IDC_BUTTON_SEL1 + i), SW_HIDE) ; + s_nType[i] = CTRL_NONE ; + } + } + break ; + + case WM_CTLCOLORSTATIC : + { + HDC hdcStatic = ( HDC)wParam ; + HWND hwndCtrl = ( HWND)lParam ; + int ctrlId = GetDlgCtrlID( hwndCtrl) ; + if ( ctrlId >= IDC_COLOR1 && ctrlId <= IDC_COLOR8) { + int idx = ctrlId - IDC_COLOR1 ; + if ( hColorBrush[idx] == NULL) + hColorBrush[idx] = CreateSolidBrush( selectedColor[idx]) ; + SetBkMode( hdcStatic, OPAQUE) ; + SetBkColor( hdcStatic, selectedColor[idx]) ; + return ( INT_PTR)hColorBrush[idx] ; + } + } + break ; + + case WM_COMMAND : + { + int id = LOWORD( wParam) ; + int code = HIWORD( wParam) ; + + // --- se click di un Button di selezione + if ( id >= IDC_BUTTON_SEL1 && id < IDC_BUTTON_SEL1 + MAX_CTRLS) { + int nBtnId = id ; + int nEditId = IDC_EDIT1 + ( id - IDC_BUTTON_SEL1) ; + int nActiveRow = nEditId - IDC_EDIT1 ; + // se il bottone premuto è lo stesso + if ( s_nDlgItem == nEditId) { + // se selezione attiva, allora deve terminare + if ( s_bSelectionMode) { + SetDlgItemText( hwndDlg, nBtnId, strztoW( "+")) ; + EndSelectionMode( hwndDlg) ; + } + // se selezione non attiva, allora viene attivata + else { + SetDlgItemText( hwndDlg, nBtnId, strztoW( "S")) ; + StartSelectionMode( hwndDlg, nEditId, nActiveRow) ; + } + } + // se nuovo buttone premuto + else { + // se esisteva una selezione precedente, questa viene annullata + if ( s_nDlgItem != -1) { + // recupero il Button di selezione precedente e modifico il suo testo + int nPrevBtnId = IDC_BUTTON_SEL1 + ( s_nDlgItem - IDC_EDIT1) ; + SetDlgItemText( hwndDlg, nPrevBtnId, strztoW( "+")) ; + } + // la selezione passa al comando corrente + s_nDlgItem = nEditId ; + SetDlgItemText( hwndDlg, nBtnId, strztoW( "S")) ; + // il dialogo diventa "Modeless" temporaneamente + s_nActiveRow = nEditId - IDC_EDIT1 ; + StartSelectionMode( hwndDlg, nEditId, nActiveRow) ; + } + return TRUE ; // !<-- esco + } + + // --- se comando di Preview per brush di colori mediante click del mouse + else if ( code == STN_CLICKED && id >= IDC_COLOR1 && id <= IDC_COLOR8) { + // se selezione attiva, allora deve terminare + if ( s_bSelectionMode) { + // se esisteva una selezione precedente, questa viene annullata + if ( s_nDlgItem != -1) { + // recupero il Button di selezione precedente e modifico il suo testo + int nPrevBtnId = IDC_BUTTON_SEL1 + ( s_nDlgItem - IDC_EDIT1) ; + SetDlgItemText( hwndDlg, nPrevBtnId, strztoW( "+")) ; + } + EndSelectionMode( hwndDlg) ; + } + int idx = id - IDC_COLOR1 ; + HWND hwndPreview = GetDlgItem( hwndDlg, IDC_COLOR1 + idx) ; + if ( ! hwndPreview) + return TRUE ; + // apertura ChooseColor (usa hwndDlg come owner) + CHOOSECOLOR cc = {} ; + cc.lStructSize = sizeof( cc) ; + cc.hwndOwner = hwndDlg ; + cc.lpCustColors = s_CustomColors ; + cc.rgbResult = selectedColor[idx] ; + cc.Flags = CC_FULLOPEN | CC_RGBINIT ; + if ( ChooseColor( &cc)) { + selectedColor[idx] = cc.rgbResult ; + // salvo nel file Ini i nuovi colori personalizzati + string sIniFile = ExeGetIniFile() ; + if ( ! sIniFile.empty()) { + string sOut = "" ; + for ( int j = 0 ; j < ssize( s_CustomColors) && j < 16 ; ++ j) + sOut += ToString( ( unsigned int)s_CustomColors[j]) + "," ; + sOut.pop_back() ; + WritePrivateProfileStringUtf8( SEC_SCENE, KEY_CUSTOMCOLORS, sOut.c_str(), sIniFile.c_str()) ; + } + if ( hColorBrush[idx] != NULL) + DeleteObject( hColorBrush[idx]) ; + hColorBrush[idx] = CreateSolidBrush( selectedColor[idx]) ; + InvalidateRect( hwndPreview, NULL, TRUE) ; + UpdateWindow( hwndPreview) ; + } + return TRUE ; // !<-- Esco + } + + switch ( LOWORD( wParam)) { + case IDOK : + { + // recupero il contesto impostato alla creazione della finestra (durante esecuzione WM_INITDIALOG) + DialogContext* pdcCtx = (DialogContext*)GetWindowLongPtr( hwndDlg, GWLP_USERDATA) ; + if ( pdcCtx != nullptr) { + // inizializzo i risultati + pdcCtx->vsResult.clear() ; + pdcCtx->vsResult.reserve( s_nCtrls) ; + // recupero i valori degli Item + for ( int i = 0 ; i < s_nCtrls ; ++ i) { + AtoWEX<128> wsEdit( "") ; + s_sEdit[i] = "" ; + switch ( s_nType[i]) { + case CTRL_CHECK : + s_sEdit[i] = ( IsDlgButtonChecked( hwndDlg, IDC_CHECK1 + i) == BST_CHECKED ? "1" : "0") ; + break ; + case CTRL_COMBO : + if ( GetDlgItemText( hwndDlg, IDC_COMBO1 + i, LPWSTR( wsEdit), 128) > 0) + s_sEdit[i] = wstrztoA( wsEdit) ; + break ; + case CTRL_EDIT : + if ( GetDlgItemText( hwndDlg, IDC_EDIT1 + i, LPWSTR( wsEdit), 128) > 0) + s_sEdit[i] = wstrztoA( wsEdit) ; + break ; + case CTRL_COLOR : + { + COLORREF col = selectedColor[i] ; + BYTE r = GetRValue( col), g = GetGValue( col), b = GetBValue( col) ; + char buf[16] ; + sprintf_s( buf, sizeof( buf), "%u,%u,%u", r, g, b) ; + s_sEdit[i] = buf ; + } + break ; + case CTRL_BUTTON : + if ( GetDlgItemText( hwndDlg, IDC_EDIT1 + i, LPWSTR( wsEdit), 128) > 0) + s_sEdit[i] = wstrztoA( wsEdit) ; + break ; + } + + // inserisco i valori all'interno della tabella Lua + pdcCtx->vsResult.push_back( s_sEdit[i]) ; + } + + pdcCtx->bCancelled = false ; + pdcCtx->bDone = true ; + } + + // chiudo il dialogo + OnClosingDialog( hwndDlg) ; + return TRUE ; + } + [[fallthrough]] ; + case IDCANCEL : + { + // pulizia brush colori + for ( int i = 0 ; i < MAX_CTRLS ; ++ i) { + if ( hColorBrush[i] != NULL) { + DeleteObject( hColorBrush[i]) ; + hColorBrush[i] = NULL ; + } + } + // recupero il contesto impostato alla creazione della finestra ( durante esecuzione WM_INITDIALOG) e se esiste lo rimuovo + DialogContext* pdcCtx = ( DialogContext*)GetWindowLongPtr( hwndDlg, GWLP_USERDATA) ; + if ( pdcCtx != nullptr) { + pdcCtx->bCancelled = true ; + pdcCtx->bDone = true ; + } + // chiudo il dialogo + OnClosingDialog( hwndDlg) ; + return TRUE ; + } + } + + // se siamo in modalità di Selezione e il comando attivo non si riferisce ad un button di Selezione (SB), il dialogo torna modale + if ( s_bSelectionMode) { + bool bIsBS = ( id >= IDC_BUTTON_SEL1 && id < IDC_BUTTON_SEL1 + MAX_CTRLS) ; + bool bIsActiveEdit = ( id == IDC_EDIT1 + s_nActiveRow) ; + // il dialogo torna modale + if ( ! bIsBS && ! bIsActiveEdit) + EndSelectionMode( hwndDlg) ; + } + break ; + } + + case WM_CLOSE : + { + // recupero il contesto impostato alla creazione della finestra ( durante esecuzione WM_INITDIALOG) e se esiste lo rimuovo + DialogContext* pdcCtx = (DialogContext*)GetWindowLongPtr( hwndDlg, GWLP_USERDATA) ; + if ( pdcCtx != nullptr) { + pdcCtx->bCancelled = true ; + pdcCtx->bDone = true ; + } + // chiudo il dialogo + OnClosingDialog( hwndDlg) ; + return TRUE ; + } + } + + return FALSE ; +} + +//----------------------------------------------------------------------------- +bool +UpdateDialogIds( void) +{ + // verifico presenza dialogo e item di selezione + if ( s_hDlg == NULL || s_nDlgItem == -1) + return false ; + + // recupero tutti gli elementi selezionati + string sIds ; + int nId = ExeGetFirstSelectedObj() ; + while ( nId != GDB_ID_NULL) { + sIds.append( ToString( nId) + ",") ; + nId = ExeGetNextSelectedObj() ; + } + if ( ! sIds.empty()) + sIds.pop_back() ; + + // aggiorno il contenuto nel dialogo + return ( SetDlgItemText( s_hDlg, s_nDlgItem, stringtoW( sIds))) ; +} + +//------------------------------------------------------------------------------- +// LuaDialogBox (Modeless) +// +// Scopo: +// ------ +// Questa funzione implementa un dialogo "modale" dal punto di vista del codice Lua, ma "modeless" dal punto di vista della +// UI Windows quando necessario. L’obiettivo è permettere al codice Lua di rimanere sospeso finché l’utente non chiude il +// dialogo (OK/Cancel/X), mantenendo però la UI attiva e permettendo selezioni nella scena quando l’utente preme un pulsante +// SB (Select Button). +// +// Perché serve: +// ------------- +// - I dialoghi modali standard (DialogBox) bloccano completamente la UI, mentre i dialoghi modeless standard (CreateDialog) +// NON bloccano il codice Lua. +// - Le coroutine Lua (lua_yield/lua_resume) non sono utilizzabili in questo contesto perché il thread principale +// dell’applicazione non può essere sospeso tramite yield: il codice Lua non riprende correttamente e la UI non si +// aggiorna coerentemente) +// +// Come funziona: +// -------------- +// 1) Il dialogo viene creato con CreateDialogParam(), quindi la UI rimane attiva. +// 2) La funzione NON ritorna subito: entra in un message loop locale (Esattamente equivalente al loop all'interno di +// DialogBox()). Il cuore del loop è GetMessage(), che è una chiamata BLOCCANTE. Significa che il thread rimane in +// attesa senza consumare CPU finché non arriva un messaggio. Non c’è polling, non c’è busy loop. Il comportamento è +// identico a quello di un dialogo modale nativo. +// 3) Il ciclo rimane attivo finché: +// - l’utente non chiude il dialogo (OK/Cancel/X) +// - oppure la finestra viene distrutta +// In quel momento ctx->bDone viene impostato a true. +// 4) Durante la vita del dialogo, se l’utente preme un pulsante SB, il dialogo diventa temporaneamente "modeless": +// - la main window viene abilitata +// - il dialogo rimane abilitato ma passa in secondo piano logico +// - l’utente può selezionare oggetti nella scena +// Quando la selezione termina, il dialogo torna modale. +// 5) Quando il dialogo chiude, i valori vengono copiati in ctx->vsResult e ritornati a Lua come risultato della funzione. +// +// Vantaggi della soluzione: +// ------------------------- +// - Lua rimane bloccato come con un dialogo modale tradizionale. +// - La UI rimane completamente attiva. +// - Le selezioni SB (mediante Select Button) funzionano senza bloccare il programma. +// - Nessun uso di coroutine Lua (che non erano affidabili in questo contesto). +// - Nessun consumo CPU anomalo. +// - Comportamento stabile e prevedibile. +// +// Nota importante: +// ---------------- +// Il dialogo NON viene mai disabilitato durante la selezione SB (Select Button). Solo la main window viene +// abilitata temporaneamente. +// Questo è fondamentale: disabilitare il dialogo impedirebbe all’utente di poterlo riselezionare dopo la selezione nella scena. +// +//------------------------------------------------------------------------------- +int +LuaDialogBox( lua_State* L) +{ + // 1 .. 9 parametri : sCaption, { sText, sDefault}, ... + LuaCheckParam( L, 1, s_sCaption) + s_nCtrls = 0 ; + for ( int i = 0 ; i < MAX_CTRLS ; ++ i) { + STRVECTOR vData ; + if ( LuaGetParam( L, 2 + i, vData) && ssize( vData) >= 2) { + s_sText[i] = Trim( vData[0]) ; + s_sEdit[i] = Trim( vData[1]) ; + ++ s_nCtrls ; + } + else + break ; + } + LuaClearStack( L) ; + + // se UI disabilitata, esco + if ( ! ExeGetEnableUI()) { + LuaSetParam( L) ; + return 1 ; + } + + // se dialogo già aperto, errore + if ( s_hDlg != NULL) { + LuaSetParam( L) ; + return 1 ; + } + + // definizione del contesto + DialogContext dcCtx ; + + // creazione di un dialogo Modeless + HWND hMainWnd = ExeGetMainWindowHandle() ; + s_nDlgItem = -1 ; + s_hDlg = ( CreateDialogParam( GetModuleIstance(), MAKEINTRESOURCE( IDD_LUADLG), hMainWnd, + (DLGPROC)DialogBoxProc, (LPARAM) &dcCtx)) ; + if ( s_hDlg == NULL) { + LuaSetParam( L) ; + return 1 ; + } + + // porto la finestra in primo piano + ShowWindow( s_hDlg, SW_SHOW) ; + SetForegroundWindow( s_hDlg) ; + + // contesto corrente + RECT rctWnd{ 0, 0, 0, 0} ; + GseContext* pGseCtx = GetCurrGseContext() ; + if ( pGseCtx != nullptr) + GetWindowRect( pGseCtx->m_hWnd, &rctWnd) ; + + // message Loop + MSG msg ; + while ( ! dcCtx.bDone && IsWindow( s_hDlg)) { + if ( GetMessage( &msg, NULL, 0, 0) <= 0) + break ; + if ( ! IsDialogMessage( s_hDlg, &msg) && + ( ! s_bSelectionMode || PtInRect( &rctWnd, msg.pt) != FALSE)) { + TranslateMessage( &msg) ; + DispatchMessage( &msg) ; + } + } + + // ritorno i valori lua + if ( dcCtx.bCancelled) + LuaSetParam( L) ; + else + LuaSetParam( L, dcCtx.vsResult) ; + return 1 ; +} diff --git a/LUA_GenDialog.h b/LUA_GenDialog.h new file mode 100644 index 0000000..12e8abd --- /dev/null +++ b/LUA_GenDialog.h @@ -0,0 +1,24 @@ +//---------------------------------------------------------------------------- +// EgalTech 2026-2026 +//---------------------------------------------------------------------------- +// File : LUA_GenDialog.h Data : 06.07.2026 Versione : 3.1g2 +// Contenuto : Prototipi funzioni LUA_GenDialog. +// +// +// +// Modifiche : 06.07.26 RE Creazione modulo. +// +// +//---------------------------------------------------------------------------- + +#pragma once + +#include "stdafx.h" +#include "/EgtDev/Include/EGkLuaAux.h" + +//---------------------------------------------------------------------------- +// Funzione implementazione DialogBox per lua +int LuaDialogBox( lua_State* L) ; + +// Funzione per passaggi di parametri tra Selezione DB e dialogo non modale +bool UpdateDialogIds( void) ; diff --git a/LUA_General.cpp b/LUA_General.cpp index 00f6cef..3984365 100644 --- a/LUA_General.cpp +++ b/LUA_General.cpp @@ -16,9 +16,7 @@ #include "DllMain.h" #include "EXE.h" #include "LUA.h" -#include "GenTools.h" -#include "resource.h" -#include "AuxDialogBox.h" +#include "LUA_GenDialog.h" #include "/EgtDev/Include/ExeExecutor.h" #include "/EgtDev/Include/EGkLuaAux.h" #include "/EgtDev/Include/EGkStringUtils3d.h" @@ -31,8 +29,6 @@ #include "/EgtDev/Include/EgtIniFile.h" #include "/EgtDev/Include/EgtNumUtils.h" #include "/EgtDev/Include/EgtStringConverter.h" -#include -#include #include using namespace std ; @@ -1228,705 +1224,11 @@ LuaReleaseMutex( lua_State* L) return 1 ; } -//------------------------------------------------------------------------------- -// =================================== DIALOG =================================== -// Variabili Globali -const int OFFS_CTRLS = 40 ; -enum TYPE_CTRLS { CTRL_NONE = 0, CTRL_CHECK = 1, CTRL_COMBO = 2, CTRL_EDIT = 3, CTRL_COLOR = 4, CTRL_BUTTON = 5} ; -static COLORREF s_CustomColors[16] = {12632256} ; // 16 colori GRAY -static bool s_bCustomColorsInit = false ; // flag di inizializzazione colori -const char* SEC_SCENE = "Scene" ; -const char* KEY_CUSTOMCOLORS = "CustomColors" ; - -// Dialogo Modeless -const int MAX_CTRLS = IDC_TEXT8 - IDC_TEXT1 + 1 ; -static int s_nCtrls = 0 ; -static string s_sCaption ; -static string s_sText[MAX_CTRLS] ; -static string s_sEdit[MAX_CTRLS] ; -static int s_nType[MAX_CTRLS] ; -struct DialogContext { - bool bDone = false ; // flag per chiusura dialogo (X/Ok/Cancel) - bool bCancelled = false ; // true se X/Cancel - bool bSelecting = false ; // true quando un BS + in modalità selezione - int nLastBS = -1 ; // indice dell'ultimo BS premuto - STRVECTOR vsResult ; // valori finali da ritornare a Lua -} ; -static string s_sIdSel_ML ; -static bool s_bSelectionMode = false ; // se true allora diventa dialogo "Modeless" -static int s_nActiveRow = - 1 ; // indice della riga di "BS:" attiva - -//------------------------------------------------------------------------------- -// OnClosingDialog -// -// Scopo: -// Gestisce la chiusura del dialogo modeless in modo centralizzato e sicuro. -// Questa funzione viene chiamata quando l’utente chiude il dialogo tramite: -// - OK -// - Cancel -// - pulsante X -// - WM_CLOSE -// -// Comportamento: -// - Riabilita la finestra principale (Main Window), che era stata disabilitata durante il funzionamento -// modale del dialogo. -// -// - Distrugge la finestra del dialogo. -// -// - Riporta il focus alla finestra principale, garantendo continuità nell’interazione dell’utente. -// -// - Deseleziona tutti gli oggetti nella scena e aggiorna la vista, in modo che nessuna selezione residua -// rimanga attiva dopo la chiusura. -//------------------------------------------------------------------------------- -static void -OnClosingDialog( HWND hwndDlg) { - // reset stato globale - s_bSelectionMode = false ; - s_nActiveRow = -1 ; - nDlgModelessItem = -1 ; - // il controllo passa al Main - HWND hMain = GetParent( hwndDlg) ; - EnableWindow( hMain, TRUE) ; - DestroyWindow( hwndDlg) ; - phDlgModeless = nullptr ; - SetFocus( hMain) ; - // deseleziono tutto - ExeDeselectAll() ; - ExeDraw() ; -} - -//------------------------------------------------------------------------------- -// StartSelectionMode -// -// Scopo: -// Attiva la modalità di selezione (modeless) del dialogo. Questa modalità permette all’utente di interagire -// con la scena mentre il dialogo rimane aperto e attivo. -// -// Parametri: -// hDlg → handle del dialogo -// nEditId → ID dell’edit associato al pulsante BS premuto -// nActiveRow → indice della riga BS attiva (0..MAX_CTRLS-1) -//------------------------------------------------------------------------------- -static void -StartSelectionMode( HWND hDlg, int nEditId, int nActiveRow) -{ - // il dialogo torna modeless rispetto alla scena - HWND hMain = GetParent( hDlg) ; - EnableWindow( hMain, TRUE) ; - // resta comunque in primo piano - SetWindowPos( hDlg, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE) ; - - // disabilito tutti gli edit associati ai BS tranne che quello attivo - for ( int i = 0; i < s_nCtrls ; ++ i) { - if (s_sEdit[i].find( "BS:") == 0) { - int idEdit = IDC_EDIT1 + i ; - EnableWindow( GetDlgItem( hDlg, idEdit), ( i == nActiveRow ? TRUE : FALSE)) ; - } - } - - // seleziono solo gli elementi presenti nell'edit corrispondente - nDlgModelessItem = - 1 ; // !<-- Temporaneo [***] - ExeDeselectAll() ; // !<-- NB. avendo messo nDlgModelessItem = -1 il testo non viene rimosso - s_bSelectionMode = true ; - s_nActiveRow = nActiveRow ; - nDlgModelessItem = nEditId ; // !<-- Aggiorno [***] - AtoWEX<128> wsEdit( "") ; - GetDlgItemText( hDlg, nEditId, LPWSTR( wsEdit), 128) ; - string sIds = wstrztoA( wsEdit) ; - STRVECTOR vsIds ; - Tokenize( sIds, ",", vsIds) ; - for ( const string& sId : vsIds) { - int nId = GDB_ID_NULL ; - if ( FromString( sId, nId) && nId != GDB_ID_NULL) - ExeSelectObj( nId) ; - } - ExeDraw() ; -} - -//------------------------------------------------------------------------------- -// EndSelectionMode -// -// Scopo: -// Termina la modalità di selezione e riporta il dialogo in stato modale. -//------------------------------------------------------------------------------- -static void -EndSelectionMode( HWND hDlg) -{ - s_bSelectionMode = false ; - s_nActiveRow = -1 ; - nDlgModelessItem = -1 ; - - // disabilito tutti gli edit associati ai BS - for ( int i = 0; i < s_nCtrls ; ++ i) { - if ( s_sEdit[i].find( "BS:") == 0) { - int idEdit = IDC_EDIT1 + i ; - EnableWindow( GetDlgItem( hDlg, idEdit), FALSE) ; - SetDlgItemText( hDlg, IDC_BUTTON_SEL1 + i, stringtoW( string{ "-"})) ; - } - } - - // il dialogo torna modale - HWND hMain = GetParent( hDlg) ; - EnableWindow( hMain, FALSE) ; - SetFocus( hDlg) ; - // deseleziono tutto - ExeDeselectAll() ; //!<-- NB. avendo messo nDlgModelessItem = -1 il testo non viene rimosso - ExeDraw() ; -} - -//------------------------------------------------------------------------------- -BOOL -CALLBACK DialogBoxModelessProc( HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam) -{ - // variabili static locali per mantenere lo stato tra chiamate per selezione colori - static COLORREF selectedColor[MAX_CTRLS] = {0} ; // BLACK - static HBRUSH hColorBrush[MAX_CTRLS] = {NULL} ; // non definito - - switch ( message) { - case WM_INITDIALOG : - { - // imposto il dialogo come modale - HWND hMain = GetParent( hwndDlg) ; - EnableWindow( hMain, FALSE) ; - SetFocus( hwndDlg) ; - - // imposto il contesto della funzione per recuperalo in futuro (per la funzione di CallBack Lua) - SetWindowLongPtr( hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam) ; - - // colore di default - COLORREF defaultColor = RGB( 255, 0, 0) ; - // inizializzazione colori e brush - for ( int i = 0 ; i < MAX_CTRLS ; ++ i) { - selectedColor[i] = defaultColor ; - if ( hColorBrush[i] != NULL) - DeleteObject( hColorBrush[i]) ; - hColorBrush[i] = CreateSolidBrush( selectedColor[i]) ; - } - // dati del dialogo - HWND hwndOwner = GetParent( hwndDlg) ; - if ( hwndOwner == nullptr) - hwndOwner = GetDesktopWindow() ; - RECT rcOwner, rcDlg ; - GetWindowRect( hwndOwner, &rcOwner) ; - GetWindowRect( hwndDlg, &rcDlg) ; - // determino la posizione dell'ultima riga di dati da visualizzare - HWND hwndLR = GetDlgItem( hwndDlg, IDC_TEXT1 + Clamp( s_nCtrls, 1, MAX_CTRLS) - 1) ; - RECT rcLR ; - GetWindowRect( hwndLR, &rcLR) ; - POINT ptLR{ rcLR.left, rcLR.top} ; - ScreenToClient( hwndDlg, &ptLR) ; - // centro e scalo il dialogo - int nNewH = rcLR.top - rcOwner.top + 2 * OFFS_CTRLS ; // non chiaro perchè va sottratto rcOwner.top ma funziona sempre - SetWindowPos( hwndDlg, HWND_TOP, - ( rcOwner.left + rcOwner.right - ( rcDlg.right - rcDlg.left)) / 2, - ( rcOwner.top + rcOwner.bottom - nNewH) / 2, - ( rcDlg.right - rcDlg.left), - nNewH, 0) ; - // riposiziono il bottone Ok - HWND hwndOk = GetDlgItem( hwndDlg, IDOK) ; - RECT rcOk ; - GetWindowRect( hwndOk, &rcOk) ; - POINT ptOk{ rcOk.left, rcOk.top} ; - ScreenToClient( hwndDlg, &ptOk) ; - SetWindowPos( hwndOk, hwndDlg, ptOk.x, ptLR.y + OFFS_CTRLS, 0, 0, SWP_NOSIZE | SWP_NOZORDER) ; - // riposiziono il bottone Cancel - HWND hwndCl = GetDlgItem( hwndDlg, IDCANCEL) ; - RECT rcCl ; - GetWindowRect( hwndCl, &rcCl) ; - POINT ptCl{ rcCl.left, rcCl.top} ; - ScreenToClient( hwndDlg, &ptCl) ; - SetWindowPos( hwndCl, hwndDlg, ptCl.x, ptLR.y + OFFS_CTRLS, 0, 0, SWP_NOSIZE | SWP_NOZORDER) ; - // stile finestra - LONG style = GetWindowLong( hwndDlg, GWL_STYLE) ; - style |= WS_SYSMENU ; - SetWindowLong( hwndDlg, GWL_STYLE, style) ; - } - - // assegno titolo del dialogo - SetWindowText( hwndDlg, stringtoW( s_sCaption)) ; - - // assegno testi e valori di default e nascondo linee dei controlli non usati - for ( int i = 0 ; i < MAX_CTRLS ; ++ i) { - if ( i < s_nCtrls) { - - // imposto il testo nella riga corrente - SetDlgItemText( hwndDlg, IDC_TEXT1 + i, stringtoW( s_sText[i])) ; - - // --- se CheckBox - if ( s_sEdit[i].find( "CK:") == 0) { - bool bChecked = false ; - FromString( s_sEdit[i].substr( 3), bChecked) ; - CheckDlgButton( hwndDlg, IDC_CHECK1 + i, ( bChecked ? BST_CHECKED : BST_UNCHECKED)) ; - ShowWindow( GetDlgItem( hwndDlg, IDC_EDIT1 + i), SW_HIDE) ; // nascondo Edit - ShowWindow( GetDlgItem( hwndDlg, IDC_COMBO1 + i), SW_HIDE) ; // nascondo Combo - ShowWindow( GetDlgItem( hwndDlg, IDC_BUTTON_SEL1 + i), SW_HIDE) ; // nascondo Button di selezione - s_nType[i] = CTRL_CHECK ; - } - - // --- se ComboBox - else if ( s_sEdit[i].find( "CB:") == 0) { - string sList = s_sEdit[i].substr( 3) ; - STRVECTOR vsVal ; - Tokenize( sList, ",", vsVal) ; - int nSel = 0 ; - HWND hwndCBox = GetDlgItem( hwndDlg, IDC_COMBO1 + i) ; - for ( int j = 0 ; j < ssize( vsVal) ; ++ j) { - string sItem ; - if ( vsVal[j][0] == '*') { - nSel = j ; - sItem = vsVal[j].substr( 1) ; - } - else - sItem = vsVal[j] ; - ComboBox_AddString( hwndCBox, stringtoW( sItem)) ; - } - ComboBox_SetCurSel( hwndCBox, nSel) ; - int nEditHeight = int( SendMessage( hwndCBox, CB_GETITEMHEIGHT, (WPARAM)-1, 0)) ; - int nItemHeight = int( SendMessage( hwndCBox, CB_GETITEMHEIGHT, 0, 0)) ; - int nTotalHeight = nEditHeight + nItemHeight * ( ssize( vsVal) + 1) ; - RECT rc ; GetWindowRect( hwndCBox, &rc) ; - HWND parent = GetParent(hwndCBox) ; MapWindowPoints( NULL, parent, (LPPOINT)&rc, 2) ; - SetWindowPos( hwndCBox, NULL, rc.left, rc.top, rc.right - rc.left, nTotalHeight, SWP_NOZORDER) ; - ShowWindow( GetDlgItem( hwndDlg, IDC_EDIT1 + i), SW_HIDE) ; // nascondo Edit - ShowWindow( GetDlgItem( hwndDlg, IDC_CHECK1 + i), SW_HIDE) ; // nascondo CheckBox - ShowWindow( GetDlgItem( hwndDlg, IDC_BUTTON_SEL1 + i), SW_HIDE) ; // nascondo Button di selezione - s_nType[i] = CTRL_COMBO ; - } - - // --- se ColorPicker - else if ( s_sEdit[i].find( "CP:") == 0) { - string sVal = s_sEdit[i].substr( 3) ; - Color CColor ; - if ( ! FromString( sVal, CColor)) - CColor = INVISIBLE ; - selectedColor[i] = RGB( CColor.GetIntRed(), CColor.GetIntGreen(), CColor.GetIntBlue()) ; - if ( hColorBrush[i] != NULL) - DeleteObject( hColorBrush[i]) ; - hColorBrush[i] = CreateSolidBrush( selectedColor[i]) ; - // recupero i valori dei colori personalizzati dal file .ini - if ( ! s_bCustomColorsInit) { - // recupero il file Ini - string sIniFile = ExeGetIniFile() ; - if ( ! sIniFile.empty()) { - string sCustomVals = GetPrivateProfileStringUtf8( SEC_SCENE, KEY_CUSTOMCOLORS, "", sIniFile.c_str()) ; - STRVECTOR vsWinColors ; Tokenize( sCustomVals, ",", vsWinColors) ; - for ( int j = 0 ; j < ssize( vsWinColors) && j < 16 ; ++ j) { - unsigned int unVal ; - if ( FromString( vsWinColors[j], unVal)) { - unsigned int unR = unVal & 0xFF ; - unsigned int unG = ( unVal >> 8) & 0xFF ; - unsigned int unB = ( unVal >> 16) & 0xFF ; - s_CustomColors[j] = RGB( unR, unG, unB) ; - } - } - } - s_bCustomColorsInit = true ; - } - // nascondo qualsiasi tipo di componente - ShowWindow( GetDlgItem( hwndDlg, IDC_EDIT1 + i), SW_HIDE) ; // nascondo Edit - ShowWindow( GetDlgItem( hwndDlg, IDC_COMBO1 + i), SW_HIDE) ; // nascondo Combo - ShowWindow( GetDlgItem( hwndDlg, IDC_CHECK1 + i), SW_HIDE) ; // nascondo Check - ShowWindow( GetDlgItem( hwndDlg, IDC_BUTTON_SEL1 + i), SW_HIDE) ; // nascondo Button di selezione - - // margini per dimensione del rettangolo preview pickColor - const int MARGIN_RIGHT = 8 ; // spazio tra preview e bordo destro dialog - const int PREVIEW_HEIGHT = 20 ; // altezza del rettangolo - const int PREVIEW_MIN_WIDTH = 40 ; // larghezza minima - - // creazione della static preview - HWND hwndPreview = NULL ; - HWND hwndEdit = GetDlgItem( hwndDlg, IDC_EDIT1 + i) ; - RECT rcEdit ; GetClientRect( hwndDlg, &rcEdit) ; // coordinate client del dialog - if ( hwndEdit != nullptr && GetWindowRect( hwndEdit, &rcEdit)) { - // converti rcLabel in coordinate client - POINT ptEdit = { rcEdit.left, rcEdit.top } ; - ScreenToClient( hwndDlg, &ptEdit) ; - int x = ptEdit.x ; - int y = ptEdit.y ; - int width = rcEdit.right - rcEdit.left ; - if ( width < PREVIEW_MIN_WIDTH) - width = PREVIEW_MIN_WIDTH ; - hwndPreview = CreateWindowEx( 0, L"STATIC", NULL, - WS_CHILD | WS_VISIBLE | SS_NOTIFY | SS_CENTERIMAGE | WS_BORDER, - x, y, width, PREVIEW_HEIGHT, - hwndDlg, ( HMENU)( IDC_COLOR1 + i), - GetModuleHandle( NULL), NULL) ; - } - // pulizia elementi testuali - if ( hwndPreview) { - SetWindowText( hwndPreview, L"") ; - SetWindowPos( hwndPreview, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE) ; - InvalidateRect( hwndPreview, NULL, TRUE) ; - UpdateWindow( hwndPreview) ; - } - s_nType[i] = CTRL_COLOR ; - } - - // --- se Button di selezione - else if ( s_sEdit[i].find( "BS:") == 0) { - ShowWindow( GetDlgItem( hwndDlg, IDC_COMBO1 + i), SW_HIDE) ; // nascondo la Combo - ShowWindow( GetDlgItem( hwndDlg, IDC_CHECK1 + i), SW_HIDE) ; // nascondo la Check - SetDlgItemText( hwndDlg, IDC_BUTTON_SEL1 + i, stringtoW( string{ "-"})) ; // modalità non Selezione - EnableWindow( GetDlgItem( hwndDlg, IDC_EDIT1 + i), FALSE) ; // disattivo la Edit - // associo alla casella di testo la funzione di KeyPress su Enter - HWND hEdit = GetDlgItem( hwndDlg, IDC_EDIT1 + i) ; - if ( hEdit != nullptr) - SetWindowSubclass( hEdit, UpdateSelectionModelessDialog, 1, (DWORD_PTR)( IDC_EDIT1 + i)) ; - s_nType[i] = CTRL_BUTTON ; - } - - // --- se Testo - else { - SetDlgItemText( hwndDlg, IDC_EDIT1 + i, stringtoW( s_sEdit[i])) ; - ShowWindow( GetDlgItem( hwndDlg, IDC_COMBO1 + i), SW_HIDE) ; // nascondo Combo - ShowWindow( GetDlgItem( hwndDlg, IDC_CHECK1 + i), SW_HIDE) ; // nascondo Check - ShowWindow( GetDlgItem( hwndDlg, IDC_BUTTON_SEL1 + i), SW_HIDE) ; // nascondo Button di selezione - s_nType[i] = CTRL_EDIT ; - } - } - // se numero di controllo superiore al massimo, non visualizzo nullo ( in questo caso aggiungere elementi a Dialog in .rc) - else { - // nascondo tutto - ShowWindow( GetDlgItem( hwndDlg, IDC_TEXT1 + i), SW_HIDE) ; - ShowWindow( GetDlgItem( hwndDlg, IDC_EDIT1 + i), SW_HIDE) ; - ShowWindow( GetDlgItem( hwndDlg, IDC_COMBO1 + i), SW_HIDE) ; - ShowWindow( GetDlgItem( hwndDlg, IDC_CHECK1 + i), SW_HIDE) ; - ShowWindow( GetDlgItem( hwndDlg, IDC_BUTTON_SEL1 + i), SW_HIDE) ; - s_nType[i] = CTRL_NONE ; - } - } - break ; - - case WM_CTLCOLORSTATIC : - { - HDC hdcStatic = ( HDC)wParam ; - HWND hwndCtrl = ( HWND)lParam ; - int ctrlId = GetDlgCtrlID( hwndCtrl) ; - if ( ctrlId >= IDC_COLOR1 && ctrlId <= IDC_COLOR8) { - int idx = ctrlId - IDC_COLOR1 ; - if ( hColorBrush[idx] == NULL) - hColorBrush[idx] = CreateSolidBrush( selectedColor[idx]) ; - SetBkMode( hdcStatic, OPAQUE) ; - SetBkColor( hdcStatic, selectedColor[idx]) ; - return ( INT_PTR)hColorBrush[idx] ; - } - } - break ; - - case WM_COMMAND : { - int id = LOWORD( wParam) ; - int code = HIWORD( wParam) ; - - // --- se click di un Button di selezione - if ( id >= IDC_BUTTON_SEL1 && id < IDC_BUTTON_SEL1 + MAX_CTRLS) { - int nBtnId = id ; - int nEditId = IDC_EDIT1 + ( id - IDC_BUTTON_SEL1) ; - int nActiveRow = nEditId - IDC_EDIT1 ; - // se il bottone premuto è lo stesso - if ( nDlgModelessItem == nEditId) { - // se selezione attiva, allora deve terminare - if ( s_bSelectionMode) { - SetDlgItemText( hwndDlg, nBtnId, stringtoW( string{ "-"})) ; - EndSelectionMode( hwndDlg) ; - } - // se selezione non attiva, allora viene attivata - else { - SetDlgItemText( hwndDlg, nBtnId, stringtoW( string{ "S"})) ; - StartSelectionMode( hwndDlg, nEditId, nActiveRow) ; - } - } - // se nuovo buttone premuto - else { - // se esisteva una selezione precedente, questa viene annullata - if ( nDlgModelessItem != -1) { - // recupero il Button di selezione precedente e modifico il suo testo - int nPrevBtnId = IDC_BUTTON_SEL1 + ( nDlgModelessItem - IDC_EDIT1) ; - SetDlgItemText( hwndDlg, nPrevBtnId, stringtoW( string{ "-"})) ; - } - // la selezione passa al comando corrente - nDlgModelessItem = nEditId ; - SetDlgItemText( hwndDlg, nBtnId, stringtoW( string{ "S"})) ; - // il dialogo diventa "Modeless" temporaneamente - s_nActiveRow = nEditId - IDC_EDIT1 ; - StartSelectionMode( hwndDlg, nEditId, nActiveRow) ; - } - return TRUE ; // !<-- esco - } - // --- se comando di Preview per brush di colori mediante click del mouse - else if ( code == STN_CLICKED && id >= IDC_COLOR1 && id <= IDC_COLOR8) { - // se selezione attiva, allora deve terminare - if ( s_bSelectionMode) { - // se esisteva una selezione precedente, questa viene annullata - if ( nDlgModelessItem != -1) { - // recupero il Button di selezione precedente e modifico il suo testo - int nPrevBtnId = IDC_BUTTON_SEL1 + ( nDlgModelessItem - IDC_EDIT1) ; - SetDlgItemText( hwndDlg, nPrevBtnId, stringtoW( string{ "-"})) ; - } - EndSelectionMode( hwndDlg) ; - } - int idx = id - IDC_COLOR1 ; - HWND hwndPreview = GetDlgItem( hwndDlg, IDC_COLOR1 + idx) ; - if ( ! hwndPreview) - return TRUE ; - // apertura ChooseColor (usa hwndDlg come owner) - CHOOSECOLOR cc = {} ; - cc.lStructSize = sizeof( cc) ; - cc.hwndOwner = hwndDlg ; - cc.lpCustColors = s_CustomColors ; - cc.rgbResult = selectedColor[idx] ; - cc.Flags = CC_FULLOPEN | CC_RGBINIT ; - if ( ChooseColor( &cc)) { - selectedColor[idx] = cc.rgbResult ; - // salvo nel file Ini i nuovi colori personalizzati - string sIniFile = ExeGetIniFile() ; - if ( ! sIniFile.empty()) { - string sOut = "" ; - for ( int j = 0 ; j < ssize( s_CustomColors) && j < 16 ; ++ j) - sOut += ToString( ( unsigned int)s_CustomColors[j]) + "," ; - sOut.pop_back() ; - WritePrivateProfileStringUtf8( SEC_SCENE, KEY_CUSTOMCOLORS, sOut.c_str(), sIniFile.c_str()) ; - } - if ( hColorBrush[idx] != NULL) - DeleteObject( hColorBrush[idx]) ; - hColorBrush[idx] = CreateSolidBrush( selectedColor[idx]) ; - InvalidateRect(hwndPreview, NULL, TRUE) ; - UpdateWindow( hwndPreview) ; - } - return TRUE ; // !<-- Esco - } - - switch ( LOWORD( wParam)) { - case IDOK : { - // recupero il contesto impostato alla creazione della finestra (durante esecuzione WM_INITDIALOG) - DialogContext* ctx = (DialogContext*)GetWindowLongPtr( hwndDlg, GWLP_USERDATA) ; - if ( ctx != nullptr) { - // inizializzo i risultati - ctx->vsResult.clear() ; - ctx->vsResult.reserve( s_nCtrls) ; - // recupero i valori degli Item - for ( int i = 0 ; i < s_nCtrls ; ++ i) { - AtoWEX<128> wsEdit( "") ; - s_sEdit[i] = "" ; - - switch ( s_nType[i]) { - case CTRL_CHECK : - s_sEdit[i] = ( IsDlgButtonChecked( hwndDlg, IDC_CHECK1 + i) == BST_CHECKED ? "1" : "0") ; - break ; - case CTRL_COMBO : - if ( GetDlgItemText( hwndDlg, IDC_COMBO1 + i, LPWSTR( wsEdit), 128) > 0) - s_sEdit[i] = wstrztoA( wsEdit) ; - break ; - case CTRL_EDIT : - if ( GetDlgItemText( hwndDlg, IDC_EDIT1 + i, LPWSTR( wsEdit), 128) > 0) - s_sEdit[i] = wstrztoA( wsEdit) ; - break ; - case CTRL_COLOR : - { - COLORREF col = selectedColor[i] ; - BYTE r = GetRValue( col), g = GetGValue( col), b = GetBValue( col) ; - char buf[16] ; - sprintf_s( buf, sizeof( buf), "%u,%u,%u", r, g, b) ; - s_sEdit[i] = buf ; - } - break ; - case CTRL_BUTTON : - if ( GetDlgItemText( hwndDlg, IDC_EDIT1 + i, LPWSTR( wsEdit), 128) > 0) - s_sEdit[i] = wstrztoA( wsEdit) ; - break ; - } - - // inserisco i valori all'interno della tabella Lua - ctx->vsResult.push_back( s_sEdit[i]) ; - } - - ctx->bCancelled = false ; - ctx->bDone = true ; - } - - // chiudo il dialogo - OnClosingDialog( hwndDlg) ; - return TRUE ; - } - [[fallthrough]] ; - case IDCANCEL : { - // pulizia brush colori - for ( int i = 0 ; i < MAX_CTRLS ; ++ i) { - if ( hColorBrush[i] != NULL) { - DeleteObject( hColorBrush[i]) ; - hColorBrush[i] = NULL ; - } - } - // recupero il contesto impostato alla creazione della finestra ( durante esecuzione WM_INITDIALOG) e se esiste lo rimuovo - DialogContext* ctx = (DialogContext*)GetWindowLongPtr( hwndDlg, GWLP_USERDATA) ; - if ( ctx != nullptr) { - ctx->bCancelled = true ; - ctx->bDone = true ; - } - // chiudo il dialogo - OnClosingDialog( hwndDlg) ; - return TRUE ; - } - } - - // se siamo in modalità di Selezione e il comando attivo non si riferisce ad un button di Selezione (BS), il dialogo torna modale - if ( s_bSelectionMode) { - bool bIsBS = ( id >= IDC_BUTTON_SEL1 && id < IDC_BUTTON_SEL1 + MAX_CTRLS) ; - bool bIsActiveEdit = ( id == IDC_EDIT1 + s_nActiveRow) ; - // il dialogo torna modale - if ( ! bIsBS && ! bIsActiveEdit) - EndSelectionMode( hwndDlg) ; - } - break ; - } - - case WM_CLOSE : { - // recupero il contesto impostato alla creazione della finestra ( durante esecuzione WM_INITDIALOG) e se esiste lo rimuovo - DialogContext* ctx = (DialogContext*)GetWindowLongPtr( hwndDlg, GWLP_USERDATA) ; - if ( ctx != nullptr) { - ctx->bCancelled = true ; - ctx->bDone = true ; - } - // chiudo il dialogo - OnClosingDialog( hwndDlg) ; - return TRUE ; - } - } - - return FALSE ; -} - -//------------------------------------------------------------------------------- -// LuaDialogBox (Modeless) -// -// Scopo: -// ------ -// Questa funzione implementa un dialogo "modale" dal punto di vista del codice Lua, ma "modeless" dal punto di vista della -// UI Windows quando necessario. L’obiettivo è permettere al codice Lua di rimanere sospeso finché l’utente non chiude il -// dialogo (OK/Cancel/X), mantenendo però la UI attiva e permettendo selezioni nella scena quando l’utente preme un pulsante -// BS (Button Selection). -// -// Perché serve: -// ------------- -// - I dialoghi modali standard (DialogBox) bloccano completamente la UI, mentre i dialoghi modeless standard (CreateDialog) -// NON bloccano il codice Lua. -// - Le coroutine Lua (lua_yield/lua_resume) non sono utilizzabili in questo contesto perché il thread principale -// dell’applicazione non può essere sospeso tramite yield: il codice Lua non riprende correttamente e la UI non si -// aggiorna coerentemente) -// -// Come funziona: -// -------------- -// 1) Il dialogo viene creato con CreateDialogParam(), quindi la UI rimane attiva. -// 2) La funzione NON ritorna subito: entra in un message loop locale (Esattamente equivalente al loop all'interno di -// DialogBox()). Il cuore del loop è GetMessage(), che è una chiamata BLOCCANTE. Significa che il thread rimane in -// attesa senza consumare CPU finché non arriva un messaggio. Non c’è polling, non c’è busy loop. Il comportamento è -// identico a quello di un dialogo modale nativo. -// 3) Il ciclo rimane attivo finché: -// - l’utente non chiude il dialogo (OK/Cancel/X) -// - oppure la finestra viene distrutta -// In quel momento ctx->bDone viene impostato a true. -// 4) Durante la vita del dialogo, se l’utente preme un pulsante BS, il dialogo diventa temporaneamente "modeless": -// - la main window viene abilitata -// - il dialogo rimane abilitato ma passa in secondo piano logico -// - l’utente può selezionare oggetti nella scena -// Quando la selezione termina, il dialogo torna modale. -// 5) Quando il dialogo chiude, i valori vengono copiati in ctx->vsResult e ritornati a Lua come risultato della funzione. -// -// Vantaggi della soluzione: -// ------------------------- -// - Lua rimane bloccato come con un dialogo modale tradizionale. -// - La UI rimane completamente attiva. -// - Le selezioni BS (mediante Button Selection) funzionano senza bloccare il programma. -// - Nessun uso di coroutine Lua (che non erano affidabili in questo contesto). -// - Nessun consumo CPU anomalo. -// - Comportamento stabile e prevedibile. -// -// Nota importante: -// ---------------- -// Il dialogo NON viene mai disabilitato durante la selezione BS (Button Selection). Solo la main window viene -// abilitata temporaneamente. -// Questo è fondamentale: disabilitare il dialogo impedirebbe all’utente di poterlo riselezionare dopo la selezione nella scena. -// -static int -LuaDialogBox( lua_State* L) -{ - // 1 .. 9 parametri : sCaption, { sText, sDefault}, ... - LuaCheckParam( L, 1, s_sCaption) - s_nCtrls = 0 ; - for ( int i = 0 ; i < MAX_CTRLS ; ++ i) { - STRVECTOR vData ; - if ( LuaGetParam( L, 2 + i, vData) && ssize( vData) >= 2) { - s_sText[i] = Trim( vData[0]) ; - s_sEdit[i] = Trim( vData[1]) ; - ++ s_nCtrls ; - } - else - break ; - } - LuaClearStack( L) ; - - // se UI disabilitata, esco - if ( ! ExeGetEnableUI()) { - LuaSetParam( L) ; - return 1 ; - } - - // se dialogo Modeless già aperto, errore - if ( phDlgModeless != nullptr) { - LuaSetParam( L) ; - return 1 ; - } - - // creazione del contesto e della Coroutine da gestire - DialogContext* ctx = new DialogContext() ; - if ( ctx == nullptr) { - LuaSetParam( L) ; - return 1 ; - } - - // creazione di un dialogo Modeless - HWND hTopWnd = ExeGetMainWindowHandle() ; - nDlgModelessItem = -1 ; - phDlgModeless = ( CreateDialogParam( GetModuleIstance(), MAKEINTRESOURCE( IDD_LUADLG), hTopWnd, - (DLGPROC)DialogBoxModelessProc, (LPARAM)ctx)) ; - if ( phDlgModeless == nullptr) { - delete( ctx) ; - ctx = nullptr ; - LuaSetParam( L) ; - return 1 ; - } - - // porto la finestra in primo piano - ShowWindow( phDlgModeless, SW_SHOW) ; - SetForegroundWindow( phDlgModeless) ; - - // message Loop Modale (ripresa del Loop come da dialogo modale) - MSG msg ; - while ( ! ctx->bDone && IsWindow( phDlgModeless)) { - if ( GetMessage( &msg, NULL, 0, 0) <= 0) - break ; - if ( ! IsDialogMessage( phDlgModeless, &msg)) { - TranslateMessage( &msg) ; - DispatchMessage( &msg) ; - } - } - - // ritorno i valori lua - if ( ctx->bCancelled) - LuaSetParam( L, false) ; - else - LuaSetParam( L, ctx->vsResult) ; - - // libero la memoria - delete ctx ; - ctx = nullptr ; - return 1 ; -} - //------------------------------------------------------------------------------- bool LuaInstallGeneral( LuaMgr& luaMgr) { - bool bOk = ( &luaMgr != nullptr) ; + bool bOk = true ; bOk = bOk && luaMgr.RegisterFunction( "print", MyPrint) ; bOk = bOk && luaMgr.RegisterFunction( "EgtEvalNumExpr", LuaEvalNumExpr) ; bOk = bOk && luaMgr.RegisterFunction( "EgtPause", LuaPause) ; diff --git a/stdafx.h b/stdafx.h index b25461e..07cd481 100644 --- a/stdafx.h +++ b/stdafx.h @@ -35,3 +35,4 @@ #pragma comment( lib, EGTEXTDIR "hiredis/Lib/hiredis" EGTLIBVER ".lib") #pragma comment( lib, EGTLIBDIR "SEgtLock" EGTLIBVER ".lib") #pragma comment( lib, "ws2_32.lib") +#pragma comment( lib, "Comctl32.lib")