diff --git a/CHANGELOG.md b/CHANGELOG.md
index e35c9149..15b8918a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,21 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
+
+## [1.0.1 / 5.40.2] - 2020-06-01
+
+### Added
+- Created a new Qt based UI names SandMan (Sandboxie Manager)
+- Resource monitor now shows the PID
+- Added basic API call log using updated BSA LogApiDll
+
+
+### Changed
+- reworked resource monitor to work with multiple event consumers
+- reworked log to work with multiple event consumers
+
+
+
## [5.40.1] - 2020-04-10
### Added
diff --git a/Sandboxie/Sandbox.sln b/Sandboxie/Sandbox.sln
index 18588707..bff10041 100644
--- a/Sandboxie/Sandbox.sln
+++ b/Sandboxie/Sandbox.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 14
-VisualStudioVersion = 14.0.25420.1
+# Visual Studio 15
+VisualStudioVersion = 15.0.28307.960
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SboxSvc", "core\svc\SboxSvc.vcxproj", "{2D3DBCAE-883E-54A6-F8F6-11228D989033}"
ProjectSection(ProjectDependencies) = postProject
@@ -69,7 +69,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SbieControl", "apps\control
{8E0EAA5B-6F5B-E0E2-338A-453EF2B548E4} = {8E0EAA5B-6F5B-E0E2-338A-453EF2B548E4}
EndProjectSection
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KmdUtil", "install\kmdutil\KmdUtil.vcxproj", "{0BF4988E-2325-4426-8CDC-BD221E4FB68C}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kmdutil", "install\kmdutil\KmdUtil.vcxproj", "{0BF4988E-2325-4426-8CDC-BD221E4FB68C}"
ProjectSection(ProjectDependencies) = postProject
{8E0EAA5B-6F5B-E0E2-338A-453EF2B548E4} = {8E0EAA5B-6F5B-E0E2-338A-453EF2B548E4}
EndProjectSection
@@ -88,6 +88,17 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SboxHostDll", "SboxHostDll\
{8E0EAA5B-6F5B-E0E2-338A-453EF2B548E4} = {8E0EAA5B-6F5B-E0E2-338A-453EF2B548E4}
EndProjectSection
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{666D2193-3CF6-4358-8312-67A0C2B09E35}"
+ ProjectSection(SolutionItems) = preProject
+ ..\CHANGELOG.md = ..\CHANGELOG.md
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "com", "com", "{7495BFF6-A576-4B96-9071-10CF956368FC}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "core", "core", "{E9D1318A-FAF0-4EF8-8561-FCB03862AC99}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "apps", "apps", "{0301861F-98D8-4767-BA7D-E146DE2E0C92}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
SbieDebug|Win32 = SbieDebug|Win32
@@ -240,4 +251,23 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {2D3DBCAE-883E-54A6-F8F6-11228D989033} = {E9D1318A-FAF0-4EF8-8561-FCB03862AC99}
+ {8B9E1B9D-FB3C-3009-9196-4315871BCD73} = {0301861F-98D8-4767-BA7D-E146DE2E0C92}
+ {E40CC819-6990-DA28-3E1F-6708BC98E37B} = {7495BFF6-A576-4B96-9071-10CF956368FC}
+ {8E0EAA5B-6F5B-E0E2-338A-453EF2B548E4} = {E9D1318A-FAF0-4EF8-8561-FCB03862AC99}
+ {41453A79-CA9B-ABCA-981C-5242AFC72DDF} = {7495BFF6-A576-4B96-9071-10CF956368FC}
+ {5410C534-4858-C748-86AD-0567A2451FDE} = {7495BFF6-A576-4B96-9071-10CF956368FC}
+ {8055A629-631E-84F5-8F3C-1908F264C81D} = {7495BFF6-A576-4B96-9071-10CF956368FC}
+ {42DB5510-0268-4655-B483-B9D6E4E48D62} = {7495BFF6-A576-4B96-9071-10CF956368FC}
+ {255002EC-9FC7-422E-B497-BE2CC5012B2D} = {E9D1318A-FAF0-4EF8-8561-FCB03862AC99}
+ {4019C5EB-8D1E-40E4-B7D1-5601B4B27288} = {E9D1318A-FAF0-4EF8-8561-FCB03862AC99}
+ {D16E291A-1F8A-4B19-AE07-0AF8CB7CCBD0} = {0301861F-98D8-4767-BA7D-E146DE2E0C92}
+ {0BF4988E-2325-4426-8CDC-BD221E4FB68C} = {0301861F-98D8-4767-BA7D-E146DE2E0C92}
+ {B8D7002B-0468-44E7-93A7-94327A5D7C7A} = {0301861F-98D8-4767-BA7D-E146DE2E0C92}
+ {3A42A9F3-E0C7-4633-9570-381802D6647D} = {E9D1318A-FAF0-4EF8-8561-FCB03862AC99}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {8CC68B2E-A32E-409D-8D3F-F68AF524E29C}
+ EndGlobalSection
EndGlobal
diff --git a/Sandboxie/SandboxDrv.sln b/Sandboxie/SandboxDrv.sln
index a22b7dae..1ec480b6 100644
--- a/Sandboxie/SandboxDrv.sln
+++ b/Sandboxie/SandboxDrv.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 14
-VisualStudioVersion = 14.0.25420.1
+# Visual Studio 15
+VisualStudioVersion = 15.0.28307.1022
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SboxDrv", "core\drv\SboxDrv.vcxproj", "{4019C5EB-8D1E-40E4-B7D1-5601B4B27288}"
ProjectSection(ProjectDependencies) = postProject
@@ -27,6 +27,14 @@ Global
SbieRelease|x64 = SbieRelease|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {4019C5EB-8D1E-40E4-B7D1-5601B4B27288}.Debug|Win32.ActiveCfg = SbieDebug|Win32
+ {4019C5EB-8D1E-40E4-B7D1-5601B4B27288}.Debug|Win32.Build.0 = SbieDebug|Win32
+ {4019C5EB-8D1E-40E4-B7D1-5601B4B27288}.Debug|x64.ActiveCfg = SbieDebug|x64
+ {4019C5EB-8D1E-40E4-B7D1-5601B4B27288}.Debug|x64.Build.0 = SbieDebug|x64
+ {4019C5EB-8D1E-40E4-B7D1-5601B4B27288}.Release|Win32.ActiveCfg = SbieRelease|Win32
+ {4019C5EB-8D1E-40E4-B7D1-5601B4B27288}.Release|Win32.Build.0 = SbieRelease|Win32
+ {4019C5EB-8D1E-40E4-B7D1-5601B4B27288}.Release|x64.ActiveCfg = SbieRelease|x64
+ {4019C5EB-8D1E-40E4-B7D1-5601B4B27288}.Release|x64.Build.0 = SbieRelease|x64
{4019C5EB-8D1E-40E4-B7D1-5601B4B27288}.SbieDebug|Win32.ActiveCfg = SbieDebug|Win32
{4019C5EB-8D1E-40E4-B7D1-5601B4B27288}.SbieDebug|Win32.Build.0 = SbieDebug|Win32
{4019C5EB-8D1E-40E4-B7D1-5601B4B27288}.SbieDebug|x64.ActiveCfg = SbieDebug|x64
@@ -35,6 +43,14 @@ Global
{4019C5EB-8D1E-40E4-B7D1-5601B4B27288}.SbieRelease|Win32.Build.0 = SbieRelease|Win32
{4019C5EB-8D1E-40E4-B7D1-5601B4B27288}.SbieRelease|x64.ActiveCfg = SbieRelease|x64
{4019C5EB-8D1E-40E4-B7D1-5601B4B27288}.SbieRelease|x64.Build.0 = SbieRelease|x64
+ {63B0DDD2-5E3B-EF38-F711-9652D2EB73B3}.Debug|Win32.ActiveCfg = SbieRelease|Win32
+ {63B0DDD2-5E3B-EF38-F711-9652D2EB73B3}.Debug|Win32.Build.0 = SbieRelease|Win32
+ {63B0DDD2-5E3B-EF38-F711-9652D2EB73B3}.Debug|x64.ActiveCfg = SbieRelease|x64
+ {63B0DDD2-5E3B-EF38-F711-9652D2EB73B3}.Debug|x64.Build.0 = SbieRelease|x64
+ {63B0DDD2-5E3B-EF38-F711-9652D2EB73B3}.Release|Win32.ActiveCfg = SbieRelease|Win32
+ {63B0DDD2-5E3B-EF38-F711-9652D2EB73B3}.Release|Win32.Build.0 = SbieRelease|Win32
+ {63B0DDD2-5E3B-EF38-F711-9652D2EB73B3}.Release|x64.ActiveCfg = SbieRelease|x64
+ {63B0DDD2-5E3B-EF38-F711-9652D2EB73B3}.Release|x64.Build.0 = SbieRelease|x64
{63B0DDD2-5E3B-EF38-F711-9652D2EB73B3}.SbieDebug|Win32.ActiveCfg = SbieRelease|Win32
{63B0DDD2-5E3B-EF38-F711-9652D2EB73B3}.SbieDebug|Win32.Build.0 = SbieRelease|Win32
{63B0DDD2-5E3B-EF38-F711-9652D2EB73B3}.SbieDebug|x64.ActiveCfg = SbieRelease|x64
@@ -43,6 +59,14 @@ Global
{63B0DDD2-5E3B-EF38-F711-9652D2EB73B3}.SbieRelease|Win32.Build.0 = SbieRelease|Win32
{63B0DDD2-5E3B-EF38-F711-9652D2EB73B3}.SbieRelease|x64.ActiveCfg = SbieRelease|x64
{63B0DDD2-5E3B-EF38-F711-9652D2EB73B3}.SbieRelease|x64.Build.0 = SbieRelease|x64
+ {7BA01954-12F1-4CEE-BA97-FAD3250D9776}.Debug|Win32.ActiveCfg = SbieRelease|Win32
+ {7BA01954-12F1-4CEE-BA97-FAD3250D9776}.Debug|Win32.Build.0 = SbieRelease|Win32
+ {7BA01954-12F1-4CEE-BA97-FAD3250D9776}.Debug|x64.ActiveCfg = SbieRelease|Win32
+ {7BA01954-12F1-4CEE-BA97-FAD3250D9776}.Debug|x64.Build.0 = SbieRelease|Win32
+ {7BA01954-12F1-4CEE-BA97-FAD3250D9776}.Release|Win32.ActiveCfg = SbieRelease|Win32
+ {7BA01954-12F1-4CEE-BA97-FAD3250D9776}.Release|Win32.Build.0 = SbieRelease|Win32
+ {7BA01954-12F1-4CEE-BA97-FAD3250D9776}.Release|x64.ActiveCfg = SbieRelease|Win32
+ {7BA01954-12F1-4CEE-BA97-FAD3250D9776}.Release|x64.Build.0 = SbieRelease|Win32
{7BA01954-12F1-4CEE-BA97-FAD3250D9776}.SbieDebug|Win32.ActiveCfg = SbieRelease|Win32
{7BA01954-12F1-4CEE-BA97-FAD3250D9776}.SbieDebug|Win32.Build.0 = SbieRelease|Win32
{7BA01954-12F1-4CEE-BA97-FAD3250D9776}.SbieDebug|x64.ActiveCfg = SbieRelease|Win32
@@ -55,4 +79,7 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {806E7BFC-90B6-4D60-A232-3321EA1A60BA}
+ EndGlobalSection
EndGlobal
diff --git a/Sandboxie/apps/control/MessageDialog.cpp b/Sandboxie/apps/control/MessageDialog.cpp
index 3028e214..d8a308a2 100644
--- a/Sandboxie/apps/control/MessageDialog.cpp
+++ b/Sandboxie/apps/control/MessageDialog.cpp
@@ -94,6 +94,8 @@ CMessageDialog::CMessageDialog(CWnd *pParentWnd, int mode)
m_buf_len = (8 * 1024);
m_buf = malloc_WCHAR(m_buf_len);
+ m_last_message_number = 0;
+
if (mode == MSGDLG_NORMAL)
ReloadConf();
@@ -250,19 +252,23 @@ void CMessageDialog::OnTimer()
while (1) {
ULONG len = m_buf_len;
- LONG status = SbieApi_GetWork(CMyApp::m_session_id, m_buf, &len);
- if (status != 0)
- break;
+ ULONG message_number = m_last_message_number;
+ ULONG code = -1;
+ LONG status = SbieApi_GetMessage(&message_number, CMyApp::m_session_id, &code, m_buf, len);
+ if (status != 0)
+ break; // error or no more entries
- ULONG *type = (ULONG *)m_buf;
- if (*type != API_LOG_MESSAGE)
+ //if (message_number != m_last_message_number + 1)
+ // we missed something
+ m_last_message_number = message_number;
+
+ if (code == 0)
+ continue; // empty dummy
+
+ if (/*code == MSG_2199 &&*/ m_firsttime)
continue;
- ULONG code = type[1];
- if (code == MSG_2199 && m_firsttime)
- continue;
-
- WCHAR *str1 = (WCHAR *)&type[2];
+ WCHAR *str1 = m_buf;
ULONG str1_len = wcslen(str1);
WCHAR *str2 = str1 + str1_len + 1;
ULONG str2_len = wcslen(str2);
diff --git a/Sandboxie/apps/control/MessageDialog.h b/Sandboxie/apps/control/MessageDialog.h
index 684dca1c..7044117e 100644
--- a/Sandboxie/apps/control/MessageDialog.h
+++ b/Sandboxie/apps/control/MessageDialog.h
@@ -55,6 +55,8 @@ class CMessageDialog : public CBaseDialog
CPtrArray m_hidden;
+ ULONG m_last_message_number;
+
BOOL IsHiddenMessage(
ULONG code, const WCHAR *detail_1, const WCHAR *detail_2);
diff --git a/Sandboxie/apps/control/MonitorDialog.cpp b/Sandboxie/apps/control/MonitorDialog.cpp
index bfdc88b5..617ee099 100644
--- a/Sandboxie/apps/control/MonitorDialog.cpp
+++ b/Sandboxie/apps/control/MonitorDialog.cpp
@@ -41,6 +41,8 @@ CMonitorDialog::CMonitorDialog(CWnd *pParentWnd)
m_username[0] = L'\0';
m_username[255] = L'\0';
m_username_len = wcslen(m_username);
+
+ m_last_entry_seq_num = 0;
}
@@ -105,15 +107,22 @@ void CMonitorDialog::OnIdle()
static const WCHAR *_Separator = L" -------------------------------";
CListBox *listbox = (CListBox *)GetDlgItem(ID_MESSAGE_LIST);
- WCHAR name[280];
+ WCHAR name[300];
while (1) {
+ ULONG seq_num = m_last_entry_seq_num;
USHORT type;
- SbieApi_MonitorGet(&type, &name[12]);
+ ULONG64 pid;
+ SbieApi_MonitorGetEx(&seq_num, &type, &pid, &name[12]);
if ((! type) || (! name[12]))
break;
+ if(seq_num != m_last_entry_seq_num + 1)
+ SbieApi_Log(MSG_1242, L"Resource access logger overflow!"); // MSG_MONITOR_OVERFLOW
+ m_last_entry_seq_num = seq_num;
+
+ // privacy protection, hide username
while (m_username_len) {
WCHAR *username_ptr = wcsstr(&name[12], m_username);
if (! username_ptr)
@@ -126,12 +135,11 @@ void CMonitorDialog::OnIdle()
name[10] = L' ';
name[9] = L' ';
if (type & MONITOR_OPEN) {
- type &= ~MONITOR_OPEN;
name[9] = L'O';
} else if (type & MONITOR_DENY) {
- type &= ~MONITOR_DENY;
name[9] = L'X';
}
+ type &= 0x0FFF;
const WCHAR *PrefixPtr = _Unknown;
if (type == MONITOR_PIPE)
@@ -152,6 +160,8 @@ void CMonitorDialog::OnIdle()
PrefixPtr = _Other;
wcsncpy(name, PrefixPtr, 9);
+ wsprintf(&name[wcslen(name)], L"; PID: %I64u", pid);
+
int index = listbox->AddString(name);
WCHAR oldname[280];
diff --git a/Sandboxie/apps/control/MonitorDialog.h b/Sandboxie/apps/control/MonitorDialog.h
index 0b461293..248c4d23 100644
--- a/Sandboxie/apps/control/MonitorDialog.h
+++ b/Sandboxie/apps/control/MonitorDialog.h
@@ -30,6 +30,7 @@ class CMonitorDialog : public CBaseDialog
WCHAR m_username[256];
ULONG m_username_len;
+ ULONG m_last_entry_seq_num;
virtual BOOL OnInitDialog();
diff --git a/Sandboxie/common/my_version.h b/Sandboxie/common/my_version.h
index b7635fbe..60ce9939 100644
--- a/Sandboxie/common/my_version.h
+++ b/Sandboxie/common/my_version.h
@@ -22,7 +22,7 @@
#define MY_VERSION_BINARY 5,40
#define MY_VERSION_STRING "5.40"
-#define MY_VERSION_STRING_EX "5.40.1"
+#define MY_VERSION_STRING_EX "5.40.2"
// These #defines are used by either Resource Compiler, or by NSIC installer
#define SBIE_INSTALLER_PATH "..\\Bin\\"
diff --git a/Sandboxie/common/win32_ntddk.h b/Sandboxie/common/win32_ntddk.h
index 279fefcb..91457079 100644
--- a/Sandboxie/common/win32_ntddk.h
+++ b/Sandboxie/common/win32_ntddk.h
@@ -218,7 +218,7 @@ typedef struct _OBJECT_BASIC_INFORMATION {
} OBJECT_BASIC_INFORMATION, *POBJECT_BASIC_INFORMATION;
typedef struct _OBJECT_NAME_INFORMATION {
- UNICODE_STRING ObjectName;
+ UNICODE_STRING Name;
} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;
typedef struct __PUBLIC_OBJECT_TYPE_INFORMATION {
@@ -921,6 +921,24 @@ typedef enum _PROCESSINFOCLASS {
ProcessDebugObjectHandle, // 30
ProcessDebugFlags,
ProcessHandleTracing,
+ ProcessIoPriority,
+ ProcessExecuteFlags,
+ ProcessResourceManagement, // ProcessTlsInformation
+ ProcessCookie,
+ ProcessImageInformation,
+ ProcessCycleTime,
+ ProcessPagePriority,
+ ProcessInstrumentationCallback, // 40
+ ProcessThreadStackAllocation,
+ ProcessWorkingSetWatchEx,
+ ProcessImageFileNameWin32,
+ ProcessImageFileMapping,
+ ProcessAffinityUpdateMode,
+ ProcessMemoryAllocationMode,
+ ProcessGroupInformation,
+ ProcessTokenVirtualizationEnabled,
+ ProcessConsoleHostProcess,
+ ProcessWindowInformation, // 50
MaxProcessInfoClass // MaxProcessInfoClass should always be the last enum
} PROCESSINFOCLASS;
@@ -931,7 +949,7 @@ typedef struct _PROCESS_BASIC_INFORMATION {
LONG BasePriority; // was type KPRIORITY
ULONG_PTR UniqueProcessId;
ULONG_PTR InheritedFromUniqueProcessId;
-} PROCESS_BASIC_INFORMATION;
+} PROCESS_BASIC_INFORMATION, *PPROCESS_BASIC_INFORMATION;
typedef struct _PROCESS_IMAGE_FILE_NAME {
USHORT Length;
@@ -1241,56 +1259,110 @@ typedef enum _SYSTEM_INFORMATION_CLASS {
SystemProcessorInformation,
SystemPerformanceInformation,
SystemTimeOfDayInformation,
- SystemNotImplemented1,
+ SystemPathInformation,
SystemProcessInformation, // 5
- SystemCallCounts,
- SystemConfigurationInformation,
- SystemProcessorTimes,
- SystemGlobalFlag,
- SystemNotImplemented2, // 10
+ SystemCallCountInformation,
+ SystemDeviceInformation,
+ SystemProcessorPerformanceInformation,
+ SystemFlagsInformation,
+ SystemCallTimeInformation, // 10
SystemModuleInformation, // 11
- SystemLockInformation,
- SystemNotImplemented3,
- SystemNotImplemented4,
- SystemNotImplemented5,
+ SystemLocksInformation,
+ SystemStackTraceInformation,
+ SystemPagedPoolInformation,
+ SystemNonPagedPoolInformation,
SystemHandleInformation,
SystemObjectInformation,
- SystemPagefileInformation,
- SystemInstructionEmulationCounts,
- SystemInvalidInfoClass1,
- SystemCacheInformation,
- SystemPoolTagInformation,
- SystemProcessorStatistics,
- SystemDpcInformation,
- SystemNotImplemented6,
- SystemLoadImage,
- SystemUnloadImage,
- SystemTimeAdjustment,
- SystemNotImplemented7,
- SystemNotImplemented8,
- SystemNotImplemented9,
+ SystemPageFileInformation,
+ SystemVdmInstemulInformation,
+ SystemVdmBopInformation,
+ SystemFileCacheInformation,
+ SystemPoolTagInformation,
+ SystemInterruptInformation,
+ SystemDpcBehaviorInformation,
+ SystemFullMemoryInformation,
+ SystemLoadGdiDriverInformation,
+ SystemUnloadGdiDriverInformation,
+ SystemTimeAdjustmentInformation,
+ SystemSummaryMemoryInformation,
+ SystemMirrorMemoryInformation,
+ SystemPerformanceTraceInformation,
SystemCrashDumpInformation,
SystemExceptionInformation, // 33
SystemCrashDumpStateInformation,
SystemKernelDebuggerInformation,
SystemContextSwitchInformation,
SystemRegistryQuotaInformation, // 37
- SystemLoadAndCallImage,
- SystemPrioritySeparation,
- SystemNotImplemented10,
- SystemNotImplemented11,
- SystemInvalidInfoClass2,
- SystemInvalidInfoClass3,
- SystemTimeZoneInformation,
+ SystemExtendServiceTableInformation,
+ SystemPrioritySeperation,
+ SystemVerifierAddDriverInformation,
+ SystemVerifierRemoveDriverInformation,
+ SystemProcessorIdleInformation,
+ SystemLegacyDriverInformation,
+ SystemCurrentTimeZoneInformation,
SystemLookasideInformation, // 45
- SystemSetTimeSlipEvent,
+ SystemTimeSlipNotification,
SystemSessionCreate,
SystemSessionDetach,
- SystemInvalidInfoClass4,
+ SystemSessionInformation,
SystemRangeStartInformation, // 50
SystemVerifierInformation,
- SystemAddVerifier,
- SystemSessionProcessesInformation
+ SystemVerifierThunkExtend,
+ SystemSessionProcessInformation,
+ SystemLoadGdiDriverInSystemSpace,
+ SystemNumaProcessorMap,
+ SystemPrefetcherInformation,
+ SystemExtendedProcessInformation,
+ SystemRecommendedSharedDataAlignment,
+ SystemComPlusPackage,
+ SystemNumaAvailableMemory,
+ SystemProcessorPowerInformation,
+ SystemEmulationBasicInformation, // WOW64
+ SystemEmulationProcessorInformation, // WOW64
+ SystemExtendedHandleInformation,
+ SystemLostDelayedWriteInformation,
+ SystemBigPoolInformation,
+ SystemSessionPoolTagInformation,
+ SystemSessionMappedViewInformation,
+ SystemHotpatchInformation,
+ SystemObjectSecurityMode,
+ SystemWatchdogTimerHandler,
+ SystemWatchdogTimerInformation,
+ SystemLogicalProcessorInformation,
+ SystemWow64SharedInformationObsolete,
+ SystemRegisterFirmwareTableInformationHandler,
+ SystemFirmwareTableInformation,
+ SystemModuleInformationEx,
+ SystemVerifierTriageInformation,
+ SystemSuperfetchInformation,
+ SystemMemoryListInformation,
+ SystemFileCacheInformationEx,
+ SystemThreadPriorityClientIdInformation,
+ SystemProcessorIdleCycleTimeInformation,
+ SystemVerifierCancellationInformation,
+ SystemProcessorPowerInformationEx,
+ SystemRefTraceInformation,
+ SystemSpecialPoolInformation,
+ SystemProcessIdInformation,
+ SystemErrorPortInformation,
+ SystemBootEnvironmentInformation,
+ SystemHypervisorInformation,
+ SystemVerifierInformationEx,
+ SystemTimeZoneInformation,
+ SystemImageFileExecutionOptionsInformation,
+ SystemCoverageInformation,
+ SystemPrefetchPatchInformation,
+ SystemVerifierFaultsInformation,
+ SystemSystemPartitionInformation,
+ SystemSystemDiskInformation,
+ SystemProcessorPerformanceDistribution,
+ SystemNumaProximityNodeInformation,
+ SystemDynamicTimeZoneInformation,
+ SystemCodeIntegrityInformation,
+ SystemProcessorMicrocodeUpdateInformation,
+ SystemProcessorBrandString,
+ SystemVirtualAddressInformation,
+ MaxSystemInfoClass
} SYSTEM_INFORMATION_CLASS;
typedef struct _MODULE_INFO {
@@ -1711,10 +1783,11 @@ __declspec(dllimport) NTSTATUS NtUnloadDriver(
//---------------------------------------------------------------------------
typedef enum _MEMORY_INFORMATION_CLASS {
- MemoryBasicInformation,
- MemoryWorkingSetList,
- MemorySectionName,
- MemoryBasicVlmInformation
+ MemoryBasicInformation,
+ MemoryWorkingSetInformation,
+ MemoryMappedFilenameInformation,
+ MemoryRegionInformation,
+ MemoryWorkingSetExInformation
} MEMORY_INFORMATION_CLASS;
__declspec(dllimport) NTSTATUS NtAllocateVirtualMemory(
diff --git a/Sandboxie/core/dll/SboxDll32.def b/Sandboxie/core/dll/SboxDll32.def
index 7dec6ca8..c8b219e2 100644
--- a/Sandboxie/core/dll/SboxDll32.def
+++ b/Sandboxie/core/dll/SboxDll32.def
@@ -26,7 +26,8 @@ SbieApi_GetFileName=_SbieApi_GetFileName@12
SbieApi_GetHomePath=_SbieApi_GetHomePath@16
SbieApi_GetUnmountHive=_SbieApi_GetUnmountHive@4
SbieApi_GetVersion=_SbieApi_GetVersion@4
-SbieApi_GetWork=_SbieApi_GetWork@12
+;;; SbieApi_GetWork=_SbieApi_GetWork@12
+SbieApi_GetMessage=_SbieApi_GetMessage@20
SbieApi_HookTramp=_SbieApi_HookTramp@8
@@ -34,6 +35,7 @@ SbieApi_IsBoxEnabled=_SbieApi_IsBoxEnabled@4
SbieApi_MonitorControl=_SbieApi_MonitorControl@8
SbieApi_MonitorGet=_SbieApi_MonitorGet@8
+SbieApi_MonitorGetEx=_SbieApi_MonitorGetEx@16
SbieApi_MonitorPut=_SbieApi_MonitorPut@8
SbieApi_MonitorPut2=_SbieApi_MonitorPut2@12
diff --git a/Sandboxie/core/dll/debug.c b/Sandboxie/core/dll/debug.c
index 1f8367b2..9c34e114 100644
--- a/Sandboxie/core/dll/debug.c
+++ b/Sandboxie/core/dll/debug.c
@@ -202,6 +202,7 @@ _FX int Debug_Init(void)
// break
//
+ OutputDebugString(L"Dll_ImageName: ");
OutputDebugString(Dll_ImageName);
//if (_wcsicmp(Dll_ImageName, L"explorer.exe") == 0) {
@@ -223,7 +224,6 @@ _FX int Debug_Init(void)
if (0)
#endif
{
-
while (! IsDebuggerPresent()) {
OutputDebugString(L"BREAK\n");
Sleep(500);
diff --git a/Sandboxie/core/dll/debug.h b/Sandboxie/core/dll/debug.h
index 0cae8582..2f34f52b 100644
--- a/Sandboxie/core/dll/debug.h
+++ b/Sandboxie/core/dll/debug.h
@@ -28,6 +28,7 @@
#undef WITH_DEBUG
+//#define WITH_DEBUG
//---------------------------------------------------------------------------
@@ -35,7 +36,8 @@
#ifdef WITH_DEBUG
-#define BREAK_IMAGE_1 L"java.exe" // L"jp2launcher.exe"
+//#define BREAK_IMAGE_1 L"java.exe" // L"jp2launcher.exe"
+#define BREAK_IMAGE_1 L"TestTarget.exe" // L"jp2launcher.exe"
int Debug_Init(void);
diff --git a/Sandboxie/core/dll/dllmain.c b/Sandboxie/core/dll/dllmain.c
index 095f5462..92125cea 100644
--- a/Sandboxie/core/dll/dllmain.c
+++ b/Sandboxie/core/dll/dllmain.c
@@ -211,6 +211,10 @@ _FX void Dll_InitInjected(void)
ULONG BoxKeyPathLen;
ULONG BoxIpcPathLen;
+#ifdef WITH_DEBUG
+ OutputDebugString(L"SbieDll: Dll_InitInjected");
+#endif WITH_DEBUG
+
//
// confirm the process is sandboxed before going further
//
@@ -690,7 +694,7 @@ _FX ULONG_PTR Dll_Ordinal1(
{
Ldr_Inject_Init(bHostInject);
}
-
+
//
// conclude the detour by passing control back to the original
// RtlFindActivationContextSectionString. the detour code used
diff --git a/Sandboxie/core/dll/file.c b/Sandboxie/core/dll/file.c
index 0de256a3..a08a62ff 100644
--- a/Sandboxie/core/dll/file.c
+++ b/Sandboxie/core/dll/file.c
@@ -461,7 +461,7 @@ _FX NTSTATUS File_GetName(
if (! NT_SUCCESS(status))
return status;
- uni = &((OBJECT_NAME_INFORMATION *)name)->ObjectName;
+ uni = &((OBJECT_NAME_INFORMATION *)name)->Name;
#ifdef WOW64_FS_REDIR
//
diff --git a/Sandboxie/core/dll/file_init.c b/Sandboxie/core/dll/file_init.c
index 3d98c1f3..3e6eb3f2 100644
--- a/Sandboxie/core/dll/file_init.c
+++ b/Sandboxie/core/dll/file_init.c
@@ -1730,7 +1730,7 @@ _FX void File_GetSetDeviceMap(WCHAR *DeviceMap96)
} else {
UNICODE_STRING *uni =
- &((OBJECT_NAME_INFORMATION *)dirname)->ObjectName;
+ &((OBJECT_NAME_INFORMATION *)dirname)->Name;
length = uni->Length / sizeof(WCHAR);
if (length > 95)
length = 95;
diff --git a/Sandboxie/core/dll/ipc.c b/Sandboxie/core/dll/ipc.c
index 333b809d..82de4581 100644
--- a/Sandboxie/core/dll/ipc.c
+++ b/Sandboxie/core/dll/ipc.c
@@ -575,7 +575,7 @@ _FX NTSTATUS Ipc_GetName(
if (! NT_SUCCESS(status))
return status;
- *OutTruePath = ((OBJECT_NAME_INFORMATION *)name)->ObjectName.Buffer;
+ *OutTruePath = ((OBJECT_NAME_INFORMATION *)name)->Name.Buffer;
if (! *OutTruePath) {
@@ -588,7 +588,7 @@ _FX NTSTATUS Ipc_GetName(
}
name = (*OutTruePath)
- + ((OBJECT_NAME_INFORMATION *)name)->ObjectName.Length
+ + ((OBJECT_NAME_INFORMATION *)name)->Name.Length
/ sizeof(WCHAR);
if (objname_len) {
@@ -972,10 +972,10 @@ _FX void Ipc_AdjustPortPath(UNICODE_STRING *ObjectName)
status = Obj_GetObjectName(handle, name, &length);
if (NT_SUCCESS(status) &&
- name->ObjectName.Length >= ParentLength * sizeof(WCHAR) &&
- 0 == _wcsnicmp(name->ObjectName.Buffer, Buffer, ParentLength)) {
+ name->Name.Length >= ParentLength * sizeof(WCHAR) &&
+ 0 == _wcsnicmp(name->Name.Buffer, Buffer, ParentLength)) {
- wmemcpy(Buffer, name->ObjectName.Buffer, ParentLength);
+ wmemcpy(Buffer, name->Name.Buffer, ParentLength);
}
Dll_Free(name);
diff --git a/Sandboxie/core/dll/ldr_init.c b/Sandboxie/core/dll/ldr_init.c
index 63424880..e64d72bc 100644
--- a/Sandboxie/core/dll/ldr_init.c
+++ b/Sandboxie/core/dll/ldr_init.c
@@ -399,7 +399,7 @@ _FX void Ldr_FixImagePath(void)
//
// if this is a forced program, we need to get the image path from
- // the kernel (i.e. NtQueryVirtualMemory with MemorySectionName),
+ // the kernel (i.e. NtQueryVirtualMemory with MemoryMappedFilenameInformation),
// in case the image path contains symbolic links or reparse points.
// such links would have been translated by NtCreateFile if the
// program was started by another program in the sandbox. so if
@@ -471,7 +471,7 @@ _FX WCHAR *Ldr_FixImagePath_2(void)
BufferLength = 256;
NameUni = Dll_AllocTemp((ULONG)BufferLength + sizeof(WCHAR) * 2);
status = __sys_NtQueryVirtualMemory(
- NtCurrentProcess(), (void *)Ldr_ImageBase, MemorySectionName,
+ NtCurrentProcess(), (void *)Ldr_ImageBase, MemoryMappedFilenameInformation,
NameUni, BufferLength, &BufferLength);
if (status == STATUS_INFO_LENGTH_MISMATCH ||
@@ -480,7 +480,7 @@ _FX WCHAR *Ldr_FixImagePath_2(void)
Dll_Free(NameUni);
NameUni = Dll_AllocTemp((ULONG)BufferLength + sizeof(WCHAR) * 2);
status = __sys_NtQueryVirtualMemory(
- NtCurrentProcess(), (void *)Ldr_ImageBase, MemorySectionName,
+ NtCurrentProcess(), (void *)Ldr_ImageBase, MemoryMappedFilenameInformation,
NameUni, BufferLength, &BufferLength);
}
diff --git a/Sandboxie/core/dll/obj.c b/Sandboxie/core/dll/obj.c
index a2bc6974..811f22fd 100644
--- a/Sandboxie/core/dll/obj.c
+++ b/Sandboxie/core/dll/obj.c
@@ -308,7 +308,7 @@ _FX NTSTATUS Obj_NtQueryVirtualMemory(
// if the request is not for an object name, then call the system
//
- if (MemoryInformationClass != MemorySectionName) {
+ if (MemoryInformationClass != MemoryMappedFilenameInformation) {
return __sys_NtQueryVirtualMemory(
ProcessHandle, BaseAddress, MemoryInformationClass,
@@ -328,7 +328,7 @@ _FX NTSTATUS Obj_NtQueryVirtualMemory(
}
status = __sys_NtQueryVirtualMemory(
- ProcessHandle, BaseAddress, MemorySectionName,
+ ProcessHandle, BaseAddress, MemoryMappedFilenameInformation,
name, maxlen, &outlen);
if (status == STATUS_INFO_LENGTH_MISMATCH ||
@@ -340,7 +340,7 @@ _FX NTSTATUS Obj_NtQueryVirtualMemory(
name = Dll_AllocTemp((ULONG)maxlen);
status = __sys_NtQueryVirtualMemory(
- ProcessHandle, BaseAddress, MemorySectionName,
+ ProcessHandle, BaseAddress, MemoryMappedFilenameInformation,
name, maxlen, &outlen);
}
diff --git a/Sandboxie/core/dll/sbieapi.c b/Sandboxie/core/dll/sbieapi.c
index a85e6762..2f848278 100644
--- a/Sandboxie/core/dll/sbieapi.c
+++ b/Sandboxie/core/dll/sbieapi.c
@@ -252,7 +252,7 @@ _FX LONG SbieApi_GetVersion(
// SbieApi_GetWork
//---------------------------------------------------------------------------
-
+/*
_FX LONG SbieApi_GetWork(
ULONG SessionId,
void *Buffer,
@@ -273,6 +273,40 @@ _FX LONG SbieApi_GetWork(
return status;
}
+*/
+
+//---------------------------------------------------------------------------
+// SbieApi_GetMessage
+//---------------------------------------------------------------------------
+
+
+_FX LONG SbieApi_GetMessage(
+ ULONG* MessageNum,
+ ULONG SessionId,
+ ULONG *MessageId,
+ wchar_t *Buffer,
+ ULONG Length)
+{
+ NTSTATUS status;
+ __declspec(align(8)) UNICODE_STRING64 msgtext;
+ __declspec(align(8)) ULONG64 parms[API_NUM_ARGS];
+ API_GET_MESSAGE_ARGS *args = (API_GET_MESSAGE_ARGS *)parms;
+
+ msgtext.MaximumLength = (USHORT)Length;
+ msgtext.Buffer = (ULONG_PTR)Buffer;
+ msgtext.Length = 0;
+
+ memzero(parms, sizeof(parms));
+ args->func_code = API_GET_MESSAGE;
+ args->msg_num.val = MessageNum;
+ args->session_id.val = SessionId;
+ args->msgid.val = MessageId;
+ args->msgtext.val = &msgtext;
+
+ status = SbieApi_Ioctl(parms);
+
+ return status;
+}
//---------------------------------------------------------------------------
@@ -1411,6 +1445,40 @@ _FX LONG SbieApi_MonitorGet(
}
+//---------------------------------------------------------------------------
+// SbieApi_MonitorGetEx
+//---------------------------------------------------------------------------
+
+
+_FX LONG SbieApi_MonitorGetEx(
+ ULONG *SeqNum,
+ USHORT *Type,
+ ULONG64 *Pid,
+ WCHAR *Name) // WCHAR [256]
+{
+ NTSTATUS status;
+ __declspec(align(8)) ULONG64 parms[API_NUM_ARGS];
+ API_MONITOR_GET_EX_ARGS *args = (API_MONITOR_GET_EX_ARGS *)parms;
+
+ args->func_code = API_MONITOR_GET_EX;
+ args->name_seq.val64 = (ULONG64)(ULONG_PTR)SeqNum;
+ args->name_type.val64 = (ULONG64)(ULONG_PTR)Type;
+ args->name_pid.val64 = (ULONG64)(ULONG_PTR)Pid;
+ args->name_len.val64 = 256 * sizeof(WCHAR);
+ args->name_ptr.val64 = (ULONG64)(ULONG_PTR)Name;
+ status = SbieApi_Ioctl(parms);
+
+ if (!NT_SUCCESS(status)) {
+ if (Type)
+ *Type = 0;
+ if (Name)
+ *Name = L'\0';
+ }
+
+ return status;
+}
+
+
//---------------------------------------------------------------------------
// SbieApi_GetUnmountHive
//---------------------------------------------------------------------------
diff --git a/Sandboxie/core/dll/sbieapi.h b/Sandboxie/core/dll/sbieapi.h
index 98bb82d3..f0967261 100644
--- a/Sandboxie/core/dll/sbieapi.h
+++ b/Sandboxie/core/dll/sbieapi.h
@@ -62,11 +62,19 @@ SBIEAPI_EXPORT
SBIEAPI_EXPORT LONG SbieApi_GetVersion(
WCHAR *version_string); // WCHAR [16]
-SBIEAPI_EXPORT
+/*SBIEAPI_EXPORT
LONG SbieApi_GetWork(
ULONG SessionId,
void *Buffer,
- ULONG *Length);
+ ULONG *Length);*/
+
+SBIEAPI_EXPORT
+LONG SbieApi_GetMessage(
+ ULONG* MessageNum,
+ ULONG SessionId,
+ ULONG *MessageId,
+ wchar_t *Buffer,
+ ULONG Length);
SBIEAPI_EXPORT LONG SbieApi_Log(
ULONG msgid, const WCHAR *format, ...);
@@ -196,6 +204,13 @@ LONG SbieApi_MonitorGet(
USHORT *Type,
WCHAR *Name); // WCHAR [256]
+SBIEAPI_EXPORT
+LONG SbieApi_MonitorGetEx(
+ ULONG *SeqNum,
+ USHORT *Type,
+ ULONG64 *Pid,
+ WCHAR *Name); // WCHAR [256]
+
//---------------------------------------------------------------------------
@@ -288,8 +303,6 @@ LONG SbieApi_QuerySymbolicLink(
SBIEAPI_EXPORT
LONG SbieApi_ReloadConf(ULONG session_id);
-SBIEAPI_EXPORT
-LONG SbieApi_ReloadConf2(ULONG session_id, const WCHAR *config_path);
SBIEAPI_EXPORT
LONG SbieApi_QueryConf(
diff --git a/Sandboxie/core/dll/scm_create.c b/Sandboxie/core/dll/scm_create.c
index 833f19fe..eaeb233f 100644
--- a/Sandboxie/core/dll/scm_create.c
+++ b/Sandboxie/core/dll/scm_create.c
@@ -825,7 +825,7 @@ _FX BOOL SbieDll_StartBoxedService(const WCHAR *ServiceName, BOOLEAN WithAdd)
ULONG retries, error;
//WCHAR text[130];
- //Sbie_swprintf(text, L"StartBoxedService; name: '%s'; pid: %d", ServiceName, GetCurrentProcessId()); // fix-me: pottential buffer overflow
+ //Sbie_swprintf(text, L"StartBoxedService; name: '%s'", ServiceName); // fix-me: pottential buffer overflow
//SbieApi_MonitorPut(MONITOR_OTHER, text);
//
@@ -1096,7 +1096,7 @@ _FX BOOL Scm_StartServiceW(
return FALSE;
WCHAR text[130];
- Sbie_swprintf(text, L"StartService; name: '%s'; pid: %d", ServiceName, GetCurrentProcessId()); // fix-me: pottential buffer overflow
+ Sbie_swprintf(text, L"StartService: %s", ServiceName); // fix-me: pottential buffer overflow
SbieApi_MonitorPut(MONITOR_OTHER, text);
if (Scm_IsBoxedService(ServiceName))
@@ -1263,7 +1263,7 @@ _FX BOOL Scm_StartServiceCtrlDispatcherX(
}
//WCHAR text[130];
- //Sbie_swprintf(text, L"StartServiceCtrlDispatcher; name: '%s'; pid %d", ServiceName, GetCurrentProcessId()); // fix-me: pottential buffer overflow
+ //Sbie_swprintf(text, L"StartServiceCtrlDispatcher; name: '%s'", ServiceName); // fix-me: pottential buffer overflow
//SbieApi_MonitorPut(MONITOR_OTHER, text);
//
diff --git a/Sandboxie/core/dll/secure.c b/Sandboxie/core/dll/secure.c
index 4d7a0477..030c3b8f 100644
--- a/Sandboxie/core/dll/secure.c
+++ b/Sandboxie/core/dll/secure.c
@@ -668,7 +668,7 @@ _FX NTSTATUS Secure_NtSetSecurityObject(
OBJECT_NAME_INFORMATION *name =
(OBJECT_NAME_INFORMATION *)name_space;
status = Obj_GetObjectName(Handle, name, &name_len);
- if (NT_SUCCESS(status) && name->ObjectName.Length == 0) {
+ if (NT_SUCCESS(status) && name->Name.Length == 0) {
IsUnnamedObject = TRUE;
}
diff --git a/Sandboxie/core/dll/support.c b/Sandboxie/core/dll/support.c
index 10a71675..0518e1d8 100644
--- a/Sandboxie/core/dll/support.c
+++ b/Sandboxie/core/dll/support.c
@@ -636,6 +636,7 @@ static UCHAR Support_BuiltinDomainRid[12] = {
#endif /* SBIEDLL_FORMATMESSAGE_ONLY */
+#if 1
//---------------------------------------------------------------------------
// SbieDll_FormatMessage_2
@@ -844,3 +845,5 @@ _FX WCHAR *SbieDll_FormatMessage2(
ins[2] = ins2;
return SbieDll_FormatMessage(code, ins);
}
+
+#endif
\ No newline at end of file
diff --git a/Sandboxie/core/drv/SboxDrv.vcxproj b/Sandboxie/core/drv/SboxDrv.vcxproj
index 4bc83364..60f54022 100644
--- a/Sandboxie/core/drv/SboxDrv.vcxproj
+++ b/Sandboxie/core/drv/SboxDrv.vcxproj
@@ -304,6 +304,7 @@
+
@@ -384,6 +385,7 @@
+
diff --git a/Sandboxie/core/drv/api.c b/Sandboxie/core/drv/api.c
index 12185839..16262777 100644
--- a/Sandboxie/core/drv/api.c
+++ b/Sandboxie/core/drv/api.c
@@ -25,6 +25,7 @@
#include "util.h"
#include "hook.h"
#include "common/my_version.h"
+#include "log_buff.h"
//---------------------------------------------------------------------------
@@ -49,7 +50,7 @@ static BOOLEAN Api_FastIo_DEVICE_CONTROL(
ULONG IoControlCode, IO_STATUS_BLOCK *IoStatus,
DEVICE_OBJECT *DeviceObject);
-static void Api_DelWork(API_WORK_ITEM *work_item);
+//static void Api_DelWork(API_WORK_ITEM *work_item);
//---------------------------------------------------------------------------
@@ -59,6 +60,8 @@ static NTSTATUS Api_GetVersion(PROCESS *proc, ULONG64 *parms);
static NTSTATUS Api_LogMessage(PROCESS *proc, ULONG64 *parms);
+static NTSTATUS Api_GetMessage(PROCESS *proc, ULONG64 *parms);
+
static NTSTATUS Api_GetWork(PROCESS *proc, ULONG64 *parms);
static NTSTATUS Api_GetHomePath(PROCESS *proc, ULONG64 *parms);
@@ -92,9 +95,11 @@ volatile HANDLE Api_ServiceProcessId = NULL;
static PERESOURCE Api_LockResource = NULL;
-static LIST Api_WorkList;
+//static LIST Api_WorkList;
static BOOLEAN Api_WorkListInitialized = FALSE;
+static LOG_BUFFER* Api_LogBuffer = NULL;
+
static volatile LONG Api_UseCount = -1;
@@ -108,11 +113,17 @@ _FX BOOLEAN Api_Init(void)
NTSTATUS status;
UNICODE_STRING uni;
+ //
+ // initialize log buffer
+ //
+
+ Api_LogBuffer = log_buffer_init(8 * 8 * 1024);
+
//
// initialize work list
//
- List_Init(&Api_WorkList);
+ //List_Init(&Api_WorkList);
if (! Mem_GetLockResource(&Api_LockResource, TRUE))
return FALSE;
@@ -168,6 +179,7 @@ _FX BOOLEAN Api_Init(void)
Api_SetFunction(API_GET_VERSION, Api_GetVersion);
Api_SetFunction(API_GET_WORK, Api_GetWork);
Api_SetFunction(API_LOG_MESSAGE, Api_LogMessage);
+ Api_SetFunction(API_GET_MESSAGE, Api_GetMessage);
Api_SetFunction(API_GET_HOME_PATH, Api_GetHomePath);
Api_SetFunction(API_SET_SERVICE_PORT, Api_SetServicePort);
@@ -195,8 +207,6 @@ _FX BOOLEAN Api_Init(void)
_FX void Api_Unload(void)
{
- API_WORK_ITEM *work_item;
-
if (Api_DeviceObject) {
IoDeleteDevice(Api_DeviceObject);
Api_DeviceObject = NULL;
@@ -209,12 +219,18 @@ _FX void Api_Unload(void)
if (Api_WorkListInitialized) {
- while (1) {
+ if (Api_LogBuffer) {
+ log_buffer_free(Api_LogBuffer);
+ Api_LogBuffer = NULL;
+ }
+
+ /*API_WORK_ITEM *work_item;
+ while (1) {
work_item = List_Head(&Api_WorkList);
if (! work_item)
break;
Api_DelWork(work_item);
- }
+ }*/
Mem_FreeLockResource(&Api_LockResource);
@@ -612,6 +628,136 @@ _FX NTSTATUS Api_LogMessage(PROCESS *proc, ULONG64 *parms)
}
+//---------------------------------------------------------------------------
+// Api_AddMessage
+//---------------------------------------------------------------------------
+
+
+_FX void Api_AddMessage(
+ NTSTATUS error_code,
+ const WCHAR *string1, ULONG string1_len,
+ const WCHAR *string2, ULONG string2_len,
+ ULONG session_id)
+{
+ KIRQL irql;
+
+ if (!Api_WorkListInitialized) // if (!Api_LogBuffer)
+ return;
+
+ //
+ // add work at the end of the work list
+ //
+
+ irql = Api_EnterCriticalSection();
+
+ ULONG entry_size = sizeof(ULONG) // session_id
+ + sizeof(ULONG) // error_code
+ + (string1_len + 1) * sizeof(WCHAR)
+ + (string2_len + 1) * sizeof(WCHAR);
+
+ CHAR* write_ptr = log_buffer_push_entry((LOG_BUFFER_SIZE_T)entry_size, Api_LogBuffer);
+ if (write_ptr) {
+ //[session_id 4][error_code 4][string1 n*2][\0 2][string2 n*2][\0 2]
+ WCHAR null_char = L'\0';
+ log_buffer_push_bytes((CHAR*)&session_id, sizeof(ULONG), &write_ptr, Api_LogBuffer);
+ log_buffer_push_bytes((CHAR*)&error_code, sizeof(ULONG), &write_ptr, Api_LogBuffer);
+ log_buffer_push_bytes((CHAR*)string1, string1_len * sizeof(WCHAR), &write_ptr, Api_LogBuffer);
+ log_buffer_push_bytes((CHAR*)&null_char, sizeof(WCHAR), &write_ptr, Api_LogBuffer);
+ log_buffer_push_bytes((CHAR*)string2, string2_len * sizeof(WCHAR), &write_ptr, Api_LogBuffer);
+ log_buffer_push_bytes((CHAR*)&null_char, sizeof(WCHAR), &write_ptr, Api_LogBuffer);
+ }
+ // else // this can only happen when the entire buffer is to small to hold this entire entry
+ // if loging fails we can't log this error :/
+
+ Api_LeaveCriticalSection(irql);
+}
+
+
+//---------------------------------------------------------------------------
+// Api_GetMessage
+//---------------------------------------------------------------------------
+
+
+_FX NTSTATUS Api_GetMessage(PROCESS *proc, ULONG64 *parms)
+{
+ API_GET_MESSAGE_ARGS *args = (API_GET_MESSAGE_ARGS *)parms;
+ NTSTATUS status = STATUS_SUCCESS;
+ UNICODE_STRING64 *msgtext;
+ WCHAR *msgtext_buffer;
+ KIRQL irql;
+
+ if (proc)
+ return STATUS_NOT_IMPLEMENTED;
+
+ ProbeForRead(args->msg_num.val, sizeof(ULONG), sizeof(ULONG));
+ ProbeForWrite(args->msg_num.val, sizeof(ULONG), sizeof(ULONG));
+
+ ProbeForWrite(args->msgid.val, sizeof(ULONG), sizeof(ULONG));
+
+ msgtext = args->msgtext.val;
+ if (!msgtext)
+ return STATUS_INVALID_PARAMETER;
+ ProbeForRead(msgtext, sizeof(UNICODE_STRING64), sizeof(ULONG));
+ ProbeForWrite(msgtext, sizeof(UNICODE_STRING64), sizeof(ULONG));
+
+ msgtext_buffer = (WCHAR *)msgtext->Buffer;
+ if (!msgtext_buffer)
+ return STATUS_INVALID_PARAMETER;
+
+ irql = Api_EnterCriticalSection();
+
+ __try {
+
+ CHAR* read_ptr = log_buffer_get_next(*args->msg_num.val, Api_LogBuffer);
+
+ if (!read_ptr) {
+
+ status = STATUS_NO_MORE_ENTRIES;
+
+ } else {
+
+ LOG_BUFFER_SIZE_T entry_size = log_buffer_get_size(&read_ptr, Api_LogBuffer);
+ LOG_BUFFER_SEQ_T seq_number = log_buffer_get_seq_num(&read_ptr, Api_LogBuffer);
+ *args->msg_num.val = seq_number;
+ //[session_id 4][error_code 4][string1 n*2][\0 2][string2 n*2][\0 2]
+ ULONG session_id;
+ log_buffer_get_bytes((CHAR*)&session_id, 4, &read_ptr, Api_LogBuffer);
+
+ if (session_id == args->session_id.val) {
+
+ log_buffer_get_bytes((CHAR*)args->msgid.val, 4, &read_ptr, Api_LogBuffer);
+ SIZE_T msg_length = entry_size - (4 + 4);
+
+ if (msg_length <= msgtext->MaximumLength)
+ {
+ msgtext->Length = (USHORT)msg_length;
+ ProbeForWrite(msgtext_buffer, msg_length, sizeof(WCHAR));
+ memcpy(msgtext_buffer, read_ptr, msg_length);
+ }
+ else
+ {
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+
+ } else {
+ // this entry is not for us, so we return an empty result to maintain sequence consistency
+
+ *args->msgid.val = 0;
+
+ }
+
+ }
+
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ status = GetExceptionCode();
+ }
+
+ Api_LeaveCriticalSection(irql);
+
+ return status;
+}
+
+
//---------------------------------------------------------------------------
// Api_SendServiceMessage
//---------------------------------------------------------------------------
@@ -709,7 +855,7 @@ _FX BOOLEAN Api_SendServiceMessage(ULONG msgid, ULONG data_len, void *data)
//---------------------------------------------------------------------------
-_FX BOOLEAN Api_AddWork(API_WORK_ITEM *work_item)
+/*_FX BOOLEAN Api_AddWork(API_WORK_ITEM *work_item)
{
KIRQL irql;
@@ -734,7 +880,7 @@ _FX BOOLEAN Api_AddWork(API_WORK_ITEM *work_item)
return TRUE;
return TRUE;
-}
+}*/
//---------------------------------------------------------------------------
@@ -742,13 +888,13 @@ _FX BOOLEAN Api_AddWork(API_WORK_ITEM *work_item)
//---------------------------------------------------------------------------
-_FX void Api_DelWork(API_WORK_ITEM *work_item)
+/*_FX void Api_DelWork(API_WORK_ITEM *work_item)
{
// this assumes Api_WorkList is already locked using Api_Lock
List_Remove(&Api_WorkList, work_item);
Mem_Free(work_item, work_item->length);
-}
+}*/
//---------------------------------------------------------------------------
@@ -758,7 +904,9 @@ _FX void Api_DelWork(API_WORK_ITEM *work_item)
_FX NTSTATUS Api_GetWork(PROCESS *proc, ULONG64 *parms)
{
- API_GET_WORK_ARGS *args = (API_GET_WORK_ARGS *)parms;
+ return STATUS_NOT_IMPLEMENTED;
+
+ /*API_GET_WORK_ARGS *args = (API_GET_WORK_ARGS *)parms;
NTSTATUS status;
void *buffer_ptr;
ULONG buffer_len;
@@ -834,7 +982,7 @@ _FX NTSTATUS Api_GetWork(PROCESS *proc, ULONG64 *parms)
Api_LeaveCriticalSection(irql);
- return status;
+ return status;*/
}
diff --git a/Sandboxie/core/drv/api.h b/Sandboxie/core/drv/api.h
index 8459af45..8458c5b8 100644
--- a/Sandboxie/core/drv/api.h
+++ b/Sandboxie/core/drv/api.h
@@ -38,7 +38,7 @@
//---------------------------------------------------------------------------
-typedef struct _API_WORK_ITEM {
+/*typedef struct _API_WORK_ITEM {
LIST_ELEM list_elem;
ULONG length; // length includes both header and data
@@ -47,7 +47,7 @@ typedef struct _API_WORK_ITEM {
ULONG data[1];
-} API_WORK_ITEM;
+} API_WORK_ITEM;*/
typedef struct _Sbie_SeFilterTokenArg
{
@@ -110,7 +110,19 @@ BOOLEAN Api_SendServiceMessage(ULONG msgid, ULONG data_len, void *data);
// allocate work_item from Driver_Pool, and initialize type, length and data
//
-BOOLEAN Api_AddWork(API_WORK_ITEM *work_item);
+//BOOLEAN Api_AddWork(API_WORK_ITEM *work_item);
+
+
+//
+// Add message to log buffer
+//
+
+void Api_AddMessage(
+ NTSTATUS error_code,
+ const WCHAR *string1, ULONG string1_len,
+ const WCHAR *string2, ULONG string2_len,
+ ULONG session_id);
+
//
// Copies boxname parameter from user
diff --git a/Sandboxie/core/drv/api_defs.h b/Sandboxie/core/drv/api_defs.h
index 31744fe7..65d1b38b 100644
--- a/Sandboxie/core/drv/api_defs.h
+++ b/Sandboxie/core/drv/api_defs.h
@@ -147,6 +147,8 @@ enum {
API_GET_WPAD_PORT,
API_SET_GAME_CONFIG_STORE_PORT,
API_SET_SMART_CARD_PORT,
+ API_MONITOR_GET_EX,
+ API_GET_MESSAGE,
API_LAST
};
@@ -189,12 +191,12 @@ API_ARGS_FIELD(WCHAR *,string)
API_ARGS_CLOSE(API_GET_VERSION_ARGS)
-API_ARGS_BEGIN(API_GET_WORK_ARGS)
+/*API_ARGS_BEGIN(API_GET_WORK_ARGS)
API_ARGS_FIELD(ULONG,session_id)
API_ARGS_FIELD(void *,buffer)
API_ARGS_FIELD(ULONG,buffer_len)
API_ARGS_FIELD(ULONG *,result_len_ptr)
-API_ARGS_CLOSE(API_GET_WORK_ARGS)
+API_ARGS_CLOSE(API_GET_WORK_ARGS)*/
API_ARGS_BEGIN(API_LOG_MESSAGE_ARGS)
@@ -203,6 +205,12 @@ API_ARGS_FIELD(ULONG,msgid)
API_ARGS_FIELD(UNICODE_STRING64 *,msgtext)
API_ARGS_CLOSE(API_LOG_MESSAGE_ARGS)
+API_ARGS_BEGIN(API_GET_MESSAGE_ARGS)
+API_ARGS_FIELD(ULONG *, msg_num)
+API_ARGS_FIELD(ULONG, session_id)
+API_ARGS_FIELD(ULONG *, msgid)
+API_ARGS_FIELD(UNICODE_STRING64 *, msgtext)
+API_ARGS_CLOSE(API_GET_MESSAGE_ARGS)
API_ARGS_BEGIN(API_QUERY_PROCESS_ARGS)
API_ARGS_FIELD(HANDLE,process_id)
@@ -311,6 +319,14 @@ API_ARGS_FIELD(ULONG,name_len)
API_ARGS_FIELD(WCHAR *,name_ptr)
API_ARGS_CLOSE(API_MONITOR_GET_PUT_ARGS)
+API_ARGS_BEGIN(API_MONITOR_GET_EX_ARGS)
+API_ARGS_FIELD(ULONG *, name_seq)
+API_ARGS_FIELD(USHORT *, name_type)
+API_ARGS_FIELD(ULONG64 *, name_pid)
+API_ARGS_FIELD(ULONG, name_len)
+API_ARGS_FIELD(WCHAR *, name_ptr)
+API_ARGS_CLOSE(API_MONITOR_GET_EX_ARGS)
+
API_ARGS_BEGIN(API_MONITOR_PUT2_ARGS)
API_ARGS_FIELD(USHORT *,name_type)
API_ARGS_FIELD(ULONG,name_len)
diff --git a/Sandboxie/core/drv/file.c b/Sandboxie/core/drv/file.c
index d790df1d..f3704d2b 100644
--- a/Sandboxie/core/drv/file.c
+++ b/Sandboxie/core/drv/file.c
@@ -989,7 +989,7 @@ _FX NTSTATUS File_Generic_MyParseProc(
if (Session_MonitorCount &&
device_type != FILE_DEVICE_PHYSICAL_NETCARD)
- Session_MonitorPut(MONITOR_IGNORE, ignore_str + 4);
+ Session_MonitorPut(MONITOR_IGNORE, ignore_str + 4, proc->pid);
Mem_Free(ignore_str, ignore_str_len);
}
@@ -1504,12 +1504,11 @@ skip_due_to_home_folder:
mon_type |= MONITOR_OPEN;
else
mon_type |= MONITOR_DENY;
- Session_MonitorPut(mon_type, mon_name);
+ Session_MonitorPut(mon_type, mon_name, proc->pid);
} else if (ShouldMonitorAccess) {
- Session_MonitorPut(
- MONITOR_FILE_OR_KEY | MONITOR_DENY, Name->Name.Buffer);
+ Session_MonitorPut(MONITOR_FILE_OR_KEY | MONITOR_DENY, Name->Name.Buffer, proc->pid);
} else if (msg1313 && status == STATUS_ACCESS_DENIED
&& device_type == FILE_DEVICE_DISK
diff --git a/Sandboxie/core/drv/gui_xp.c b/Sandboxie/core/drv/gui_xp.c
index 84461781..13b3a36f 100644
--- a/Sandboxie/core/drv/gui_xp.c
+++ b/Sandboxie/core/drv/gui_xp.c
@@ -1334,7 +1334,7 @@ _FX ULONG_PTR Gui_NtUserPostThreadMessage(
--nptr; *nptr = L':';
--nptr; *nptr = L'$';
- Session_MonitorPut(mon_type, nptr);
+ Session_MonitorPut(mon_type, nptr, proc->pid);
Mem_Free(nbuf, nlen);
}
diff --git a/Sandboxie/core/drv/ipc.c b/Sandboxie/core/drv/ipc.c
index 143b4e07..bed0d356 100644
--- a/Sandboxie/core/drv/ipc.c
+++ b/Sandboxie/core/drv/ipc.c
@@ -940,7 +940,7 @@ _FX NTSTATUS Ipc_CheckGenericObject(
mon_type |= MONITOR_OPEN;
else
mon_type |= MONITOR_DENY;
- Session_MonitorPut(mon_type, mon_name);
+ Session_MonitorPut(mon_type, mon_name, proc->pid);
}
// DbgPrint("Process <%06d> Status <%08X> Object <%S>\n", proc->pid, status, Name->Name.Buffer);
diff --git a/Sandboxie/core/drv/key.c b/Sandboxie/core/drv/key.c
index f8d3eb72..03fa7b63 100644
--- a/Sandboxie/core/drv/key.c
+++ b/Sandboxie/core/drv/key.c
@@ -470,8 +470,7 @@ _FX NTSTATUS Key_MyParseProc_2(OBJ_PARSE_PROC_ARGS_2)
if (ShouldMonitorAccess) {
- Session_MonitorPut(
- MONITOR_FILE_OR_KEY | MONITOR_DENY, Name->Name.Buffer);
+ Session_MonitorPut(MONITOR_FILE_OR_KEY | MONITOR_DENY, Name->Name.Buffer, proc->pid);
}
Mem_Free(Name, NameLength);
diff --git a/Sandboxie/core/drv/log.c b/Sandboxie/core/drv/log.c
index a53a537e..d3fdff8d 100644
--- a/Sandboxie/core/drv/log.c
+++ b/Sandboxie/core/drv/log.c
@@ -25,7 +25,6 @@
#include "api.h"
#include "util.h"
-
//---------------------------------------------------------------------------
// Functions
//---------------------------------------------------------------------------
@@ -36,11 +35,11 @@ static void Log_Event_Msg(
const WCHAR *string1,
const WCHAR *string2);
-static void Log_Popup_Msg_2(
+/*static void Log_Popup_Msg_2(
NTSTATUS error_code,
const WCHAR *string1, ULONG string1_len,
const WCHAR *string2, ULONG string2_len,
- ULONG session_id);
+ ULONG session_id);*/
//---------------------------------------------------------------------------
@@ -133,14 +132,16 @@ _FX void Log_Popup_Msg(
if ((Driver_OsVersion >= DRIVER_WINDOWS_VISTA) && (session_id == 0))
session_id = 1;
- Log_Popup_Msg_2(
+ //Log_Popup_Msg_2(
+ Api_AddMessage(
error_code, string1, string1_len, string2, string2_len, session_id);
//
// log message to SbieSvc and trigger SbieSvc to wake up and collect it
//
- Log_Popup_Msg_2(
+ //Log_Popup_Msg_2(
+ Api_AddMessage(
error_code, string1, string1_len, string2, string2_len, -1);
string1_len = 0;
@@ -155,7 +156,7 @@ _FX void Log_Popup_Msg(
//---------------------------------------------------------------------------
-_FX void Log_Popup_Msg_2(
+/*_FX void Log_Popup_Msg_2(
NTSTATUS error_code,
const WCHAR *string1, ULONG string1_len,
const WCHAR *string2, ULONG string2_len,
@@ -202,7 +203,7 @@ _FX void Log_Popup_Msg_2(
Api_AddWork(work_item);
}
-}
+}*/
//---------------------------------------------------------------------------
diff --git a/Sandboxie/core/drv/session.c b/Sandboxie/core/drv/session.c
index 8ce8663a..7105ea15 100644
--- a/Sandboxie/core/drv/session.c
+++ b/Sandboxie/core/drv/session.c
@@ -26,6 +26,7 @@
#include "api.h"
#include "process.h"
#include "obj.h"
+#include "log_buff.h"
//---------------------------------------------------------------------------
@@ -70,9 +71,7 @@ struct _SESSION {
// resource monitor
//
- WCHAR *monitor_buf;
- WCHAR *monitor_read_ptr;
- WCHAR *monitor_write_ptr;
+ LOG_BUFFER* monitor_log;
};
@@ -110,6 +109,8 @@ static NTSTATUS Session_Api_MonitorPut2(PROCESS *proc, ULONG64 *parms);
static NTSTATUS Session_Api_MonitorGet(PROCESS *proc, ULONG64 *parms);
+static NTSTATUS Session_Api_MonitorGetEx(PROCESS *proc, ULONG64 *parms);
+
//---------------------------------------------------------------------------
@@ -150,6 +151,7 @@ _FX BOOLEAN Session_Init(void)
Api_SetFunction(API_MONITOR_PUT, Session_Api_MonitorPut);
Api_SetFunction(API_MONITOR_PUT2, Session_Api_MonitorPut2);
Api_SetFunction(API_MONITOR_GET, Session_Api_MonitorGet);
+ Api_SetFunction(API_MONITOR_GET_EX, Session_Api_MonitorGetEx);
//
// initialize set of recognized objects types for Session_Api_MonitorPut
@@ -338,8 +340,8 @@ _FX void Session_Cancel(HANDLE ProcessId)
while (session) {
if ((session->leader_pid == ProcessId) || (! ProcessId)) {
- if (session->monitor_buf) {
- ExFreePoolWithTag(session->monitor_buf, tzuk);
+ if (session->monitor_log) {
+ log_buffer_free(session->monitor_log);
InterlockedDecrement(&Session_MonitorCount);
}
@@ -573,7 +575,7 @@ _FX BOOLEAN Session_IsForceDisabled(ULONG SessionId)
//---------------------------------------------------------------------------
-_FX void Session_MonitorPut(USHORT type, const WCHAR *name)
+_FX void Session_MonitorPut(USHORT type, const WCHAR *name, HANDLE pid)
{
SESSION *session;
KIRQL irql;
@@ -582,41 +584,22 @@ _FX void Session_MonitorPut(USHORT type, const WCHAR *name)
if (! session)
return;
- if (session->monitor_buf && *name) {
+ if (session->monitor_log && *name) {
- WCHAR *buf0 = session->monitor_buf;
- WCHAR *wptr = session->monitor_write_ptr;
- WCHAR *rptr = session->monitor_read_ptr;
+ ULONG64 pid64 = (ULONG64)pid;
+ SIZE_T data_len = (wcslen(name) + 1) * 2;
- ULONG name_len = wcslen(name) + 1;
+ //[Type 2][PID 8][Data n*2]
+ SIZE_T entry_size = 2 + 8 + data_len;
- BOOLEAN first = TRUE;
-
- while (name_len) {
-
- if (first) {
- first = FALSE;
- *wptr = type;
- } else {
- *wptr = *name;
- ++name;
- --name_len;
- }
-
- ++wptr;
- if (wptr >= buf0 + SESSION_MONITOR_BUF_SIZE)
- wptr = buf0;
- if (wptr == rptr) {
- Log_Msg0(MSG_MONITOR_OVERFLOW);
- *buf0 = L'\0';
- wptr = buf0;
- session->monitor_read_ptr = buf0;
- break;
- }
- }
-
- *wptr = L'\0';
- session->monitor_write_ptr = wptr;
+ CHAR* write_ptr = log_buffer_push_entry((LOG_BUFFER_SIZE_T)entry_size, session->monitor_log);
+ if (write_ptr) {
+ log_buffer_push_bytes((CHAR*)&type, 2, &write_ptr, session->monitor_log);
+ log_buffer_push_bytes((CHAR*)&pid64, 8, &write_ptr, session->monitor_log);
+ log_buffer_push_bytes((CHAR*)name, data_len, &write_ptr, session->monitor_log);
+ }
+ else // this can only happen when the entire buffer is to small to hold this entire entry
+ Log_Msg0(MSG_MONITOR_OVERFLOW);
}
Session_Unlock(irql);
@@ -650,7 +633,7 @@ _FX NTSTATUS Session_Api_MonitorControl(PROCESS *proc, ULONG64 *parms)
*out_flag = FALSE;
session = Session_Get(FALSE, -1, &irql);
if (session) {
- if (session->monitor_buf)
+ if (session->monitor_log)
*out_flag = TRUE;
Session_Unlock(irql);
}
@@ -676,24 +659,18 @@ _FX NTSTATUS Session_Api_MonitorControl(PROCESS *proc, ULONG64 *parms)
session = Session_Get(FALSE, -1, &irql);
if (session) {
- if (EnableMonitor && (! session->monitor_buf)) {
+ if (EnableMonitor && (! session->monitor_log)) {
- session->monitor_buf = ExAllocatePoolWithTag(PagedPool,
- SESSION_MONITOR_BUF_SIZE * sizeof(WCHAR), tzuk);
- session->monitor_write_ptr = session->monitor_buf;
- session->monitor_read_ptr = session->monitor_buf;
- if (session->monitor_buf) {
- *session->monitor_buf = L'\0';
+ session->monitor_log = log_buffer_init(SESSION_MONITOR_BUF_SIZE * sizeof(WCHAR));
+ if (session->monitor_log) {
InterlockedIncrement(&Session_MonitorCount);
} else
Log_Msg0(MSG_1201);
- } else if ((! EnableMonitor) && session->monitor_buf) {
+ } else if ((! EnableMonitor) && session->monitor_log) {
- ExFreePoolWithTag(session->monitor_buf, tzuk);
- session->monitor_buf = NULL;
- session->monitor_write_ptr = session->monitor_buf;
- session->monitor_read_ptr = session->monitor_buf;
+ log_buffer_free(session->monitor_log);
+ session->monitor_log = NULL;
InterlockedDecrement(&Session_MonitorCount);
}
@@ -895,7 +872,7 @@ _FX NTSTATUS Session_Api_MonitorPut2(PROCESS *proc, ULONG64 *parms)
name[1] = L'\0';
}
- Session_MonitorPut(type, name);
+ Session_MonitorPut(type, name, proc->pid);
}
Mem_Free(name, 260 * sizeof(WCHAR));
@@ -908,12 +885,25 @@ _FX NTSTATUS Session_Api_MonitorPut2(PROCESS *proc, ULONG64 *parms)
// Session_Api_MonitorGet
//---------------------------------------------------------------------------
-
_FX NTSTATUS Session_Api_MonitorGet(PROCESS *proc, ULONG64 *parms)
{
- API_MONITOR_GET_PUT_ARGS *args = (API_MONITOR_GET_PUT_ARGS *)parms;
+ API_MONITOR_GET_PUT_ARGS *args = (API_MONITOR_GET_PUT_ARGS *)parms;
+ API_MONITOR_GET_EX_ARGS args2 = { args->func_code, 0, args->name_type.val64, 0, args->name_len.val64, args->name_ptr.val64 };
+
+ return Session_Api_MonitorGetEx(proc, (ULONG64*)&args2);
+}
+
+//---------------------------------------------------------------------------
+// Session_Api_MonitorGet
+//---------------------------------------------------------------------------
+
+_FX NTSTATUS Session_Api_MonitorGetEx(PROCESS *proc, ULONG64 *parms)
+{
+ API_MONITOR_GET_EX_ARGS *args = (API_MONITOR_GET_EX_ARGS *)parms;
NTSTATUS status;
+ ULONG *seq_num;
USHORT *user_type;
+ ULONG64 *user_pid;
ULONG name_len;
WCHAR *user_name;
SESSION *session;
@@ -922,9 +912,19 @@ _FX NTSTATUS Session_Api_MonitorGet(PROCESS *proc, ULONG64 *parms)
if (proc)
return STATUS_NOT_IMPLEMENTED;
+ seq_num = args->name_seq.val;
+ if (seq_num != NULL) {
+ ProbeForRead(seq_num, sizeof(ULONG), sizeof(ULONG));
+ ProbeForWrite(seq_num, sizeof(ULONG), sizeof(ULONG));
+ }
+
user_type = args->name_type.val;
ProbeForWrite(user_type, sizeof(USHORT), sizeof(USHORT));
+ user_pid = args->name_pid.val;
+ if(user_pid != NULL)
+ ProbeForWrite(user_pid, sizeof(ULONG64), sizeof(ULONG64));
+
name_len = args->name_len.val / sizeof(WCHAR);
if ((! name_len) || name_len > 256)
return STATUS_INVALID_PARAMETER;
@@ -932,6 +932,8 @@ _FX NTSTATUS Session_Api_MonitorGet(PROCESS *proc, ULONG64 *parms)
ProbeForWrite(user_name, name_len * sizeof(WCHAR), sizeof(WCHAR));
*user_type = 0;
+ if (user_pid != NULL)
+ *user_pid = 0;
*user_name = L'\0';
status = STATUS_SUCCESS;
@@ -941,39 +943,31 @@ _FX NTSTATUS Session_Api_MonitorGet(PROCESS *proc, ULONG64 *parms)
__try {
- if (session->monitor_buf) {
+ if (session->monitor_log) {
- WCHAR *buf0 = session->monitor_buf;
- WCHAR *rptr = session->monitor_read_ptr;
- BOOLEAN first = TRUE;
+ CHAR* read_ptr = NULL;
+ if (seq_num != NULL)
+ read_ptr = log_buffer_get_next(*seq_num, session->monitor_log);
+ else if (session->monitor_log->buffer_size > 0) // for compatybility with older versions we return the oldest entry
+ read_ptr = session->monitor_log->buffer_start_ptr;
- if (*rptr) {
+ if (read_ptr != NULL) {
+ LOG_BUFFER_SIZE_T entry_size = log_buffer_get_size(&read_ptr, session->monitor_log);
+ LOG_BUFFER_SEQ_T seq_number = log_buffer_get_seq_num(&read_ptr, session->monitor_log);
+ if (seq_num != NULL)
+ *seq_num = seq_number;
+ //[Type 2][PID 8][Data n*2]
+ log_buffer_get_bytes((CHAR*)user_type, 2, &read_ptr, session->monitor_log);
+ ULONG64 pid64;
+ log_buffer_get_bytes((CHAR*)&pid64, 8, &read_ptr, session->monitor_log);
+ if (user_pid != NULL)
+ *user_pid = pid64;
+ log_buffer_get_bytes((CHAR*)user_name, entry_size - (2 + 8), &read_ptr, session->monitor_log);
+ }
- while (*rptr) {
-
- if (first) {
- first = FALSE;
- *user_type = *rptr;
- } else if (name_len) {
- *user_name = *rptr;
- ++user_name;
- --name_len;
- } else
- status = STATUS_BUFFER_OVERFLOW;
-
- ++rptr;
- if (rptr >= buf0 + SESSION_MONITOR_BUF_SIZE)
- rptr = buf0;
- }
-
- ++rptr;
- if (rptr >= buf0 + SESSION_MONITOR_BUF_SIZE)
- rptr = buf0;
- session->monitor_read_ptr = rptr;
-
- if (name_len)
- *user_name = L'\0';
- }
+ // for compatybility with older versions we fall back to clearing the returned entry
+ if (seq_num != NULL)
+ log_buffer_pop_entry(session->monitor_log);
}
} __except (EXCEPTION_EXECUTE_HANDLER) {
diff --git a/Sandboxie/core/drv/session.h b/Sandboxie/core/drv/session.h
index 25a6da99..86dd5e4d 100644
--- a/Sandboxie/core/drv/session.h
+++ b/Sandboxie/core/drv/session.h
@@ -41,7 +41,7 @@ void Session_Cancel(HANDLE ProcessId);
BOOLEAN Session_IsForceDisabled(ULONG SessionId);
-void Session_MonitorPut(USHORT type, const WCHAR *name);
+void Session_MonitorPut(USHORT type, const WCHAR *name, HANDLE pid);
//---------------------------------------------------------------------------
diff --git a/Sandboxie/core/drv/thread.c b/Sandboxie/core/drv/thread.c
index 9aebfbeb..7dd38c28 100644
--- a/Sandboxie/core/drv/thread.c
+++ b/Sandboxie/core/drv/thread.c
@@ -1034,7 +1034,7 @@ _FX NTSTATUS Thread_CheckObject_Common(
--nptr; *nptr = L':';
--nptr; *nptr = L'$';
- Session_MonitorPut(mon_type, nptr);
+ Session_MonitorPut(mon_type, nptr, proc->pid);
Mem_Free(nbuf, nlen);
}
diff --git a/Sandboxie/core/svc/DriverAssist.cpp b/Sandboxie/core/svc/DriverAssist.cpp
index 07f24e1c..553b0b7d 100644
--- a/Sandboxie/core/svc/DriverAssist.cpp
+++ b/Sandboxie/core/svc/DriverAssist.cpp
@@ -58,6 +58,8 @@ DriverAssist::DriverAssist()
m_Threads = NULL;
m_DriverReady = false;
+ m_last_message_number = 0;
+
InitializeCriticalSection(&m_LogMessage_CritSec);
InitializeCriticalSection(&m_critSecHostInjectedSvcs);
}
diff --git a/Sandboxie/core/svc/DriverAssist.h b/Sandboxie/core/svc/DriverAssist.h
index 7b242249..5ccd62d6 100644
--- a/Sandboxie/core/svc/DriverAssist.h
+++ b/Sandboxie/core/svc/DriverAssist.h
@@ -96,7 +96,7 @@ private:
void LogMessage();
- void LogMessage_Single(void *data);
+ void LogMessage_Single(ULONG code, wchar_t* data);
void LogMessage_Multi(ULONG msgid, const WCHAR *path, const WCHAR *text);
void LogMessage_Write(const WCHAR *path, const WCHAR *text);
@@ -139,6 +139,8 @@ private:
volatile bool m_DriverReady;
+ ULONG m_last_message_number;
+
//
// critical sections
//
diff --git a/Sandboxie/core/svc/DriverAssistLog.cpp b/Sandboxie/core/svc/DriverAssistLog.cpp
index 56718e40..b521908b 100644
--- a/Sandboxie/core/svc/DriverAssistLog.cpp
+++ b/Sandboxie/core/svc/DriverAssistLog.cpp
@@ -50,7 +50,9 @@ void DriverAssist::LogMessage()
break;
ULONG len = m_workItemLen;
- ULONG status = SbieApi_GetWork(-1, m_workItemBuf, &len);
+ ULONG message_number = m_last_message_number;
+ ULONG code = -1;
+ ULONG status = SbieApi_GetMessage(&message_number, -1, &code, (wchar_t*)m_workItemBuf, len);
if (status == STATUS_BUFFER_TOO_SMALL) {
HeapFree(GetProcessHeap(), 0, m_workItemBuf);
@@ -60,11 +62,16 @@ void DriverAssist::LogMessage()
}
if (status != 0)
- break;
+ break; // error or no more entries
- WORK_ITEM *work = (WORK_ITEM *)m_workItemBuf;
- if (work->type == API_LOG_MESSAGE)
- LogMessage_Single(work->data);
+ //if (message_number != m_last_message_number + 1)
+ // we missed something
+ m_last_message_number = message_number;
+
+ if (code == 0)
+ break; // empty dummy
+
+ LogMessage_Single(code, (wchar_t*)m_workItemBuf);
}
if (m_workItemBuf)
@@ -79,7 +86,7 @@ void DriverAssist::LogMessage()
//---------------------------------------------------------------------------
-void DriverAssist::LogMessage_Single(void *data)
+void DriverAssist::LogMessage_Single(ULONG code, wchar_t* data)
{
//
// check if logging is enabled
@@ -108,16 +115,15 @@ void DriverAssist::LogMessage_Single(void *data)
// get log message
//
- ULONG *msgidptr = (ULONG *)data;
- if (*msgidptr == MSG_2199)
+ if (code == MSG_2199)
return;
- WCHAR *str1 = (WCHAR *)(msgidptr + 1);
+ WCHAR *str1 = data;
ULONG str1_len = wcslen(str1);
WCHAR *str2 = str1 + str1_len + 1;
ULONG str2_len = wcslen(str2);
- WCHAR *text = SbieDll_FormatMessage2(*msgidptr, str1, str2);
+ WCHAR *text = SbieDll_FormatMessage2(code, str1, str2);
if (! text)
return;
@@ -152,7 +158,7 @@ void DriverAssist::LogMessage_Single(void *data)
LogMessage_Write(path, text);
- LogMessage_Multi(*msgidptr, path, text);
+ LogMessage_Multi(code, path, text);
LocalFree(text);
}
diff --git a/Sandboxie/install/ParseVersion.bat b/Sandboxie/install/ParseVersion.bat
index 11017a03..25221c7a 100644
--- a/Sandboxie/install/ParseVersion.bat
+++ b/Sandboxie/install/ParseVersion.bat
@@ -12,7 +12,7 @@ echo. > %OUTPUT%
for /F "tokens=3*" %%A in ('findstr /R "^#define.SBIE_INSTALLER_PATH\>" %INPUT%') do ( echo ^^!define SBIE_INSTALLER_PATH %%A) >> %OUTPUT%
-for /F "tokens=3*" %%A in ('findstr /R "^#define.MY_VERSION_STRING\>" %INPUT%') do ( echo ^^!define VERSION %%A) >> %OUTPUT%
+for /F "tokens=3*" %%A in ('findstr /R "^#define.MY_VERSION_STRING_EX\>" %INPUT%') do ( echo ^^!define VERSION %%A) >> %OUTPUT%
for /F "tokens=3*" %%A in ('findstr /R "^#define.MY_PRODUCT_NAME_STRING\>" %INPUT%') do ( set C=%%A %%B& echo ^^!define PRODUCT_FULL_NAME !C! & set C=!C: =!& echo ^^!define PRODUCT_NAME !C!) >> %OUTPUT%
diff --git a/Sandboxie/install/SandboxieVS.nsi b/Sandboxie/install/SandboxieVS.nsi
index f31e119c..b5534f96 100644
--- a/Sandboxie/install/SandboxieVS.nsi
+++ b/Sandboxie/install/SandboxieVS.nsi
@@ -27,8 +27,8 @@ SetCompressor /SOLID /FINAL lzma
;----------------------------------------------------------------------------
; these are the build-time config settings. Need to be cmd line args or something better.
; pick either 32 or 64 bit
-!define _BUILDARCH Win32
-;!define _BUILDARCH x64
+;!define _BUILDARCH Win32
+!define _BUILDARCH x64
; uncomment this line if you want to make the special versions that download VC Redist
;!define INCLUDE_VCREDIST_DNLD
@@ -764,21 +764,21 @@ FunctionEnd
; We download various files during install just to keep stats on activity
Function DownloadStatPng
-
- Pop $0 ; Get the parameter (file name to download)
- ${If} ${RunningX64}
- SetRegView 64
- ${EndIf}
- ReadRegStr $1 HKLM "SOFTWARE\Microsoft\Cryptography" "MachineGuid"
- StrCpy $2 "https://www.sandboxie.com/img/$0?SessionId=$1"
-
- ;NSISdl::download_quiet /TIMEOUT=3000 $2 $TEMP\$0
- inetc::get /SILENT /CONNECTTIMEOUT=5000 /RECEIVETIMEOUT=5000 $2 $TEMP\$0 /END
- Pop $0 ;Get the return value
- ;MessageBox MB_OK|MB_ICONSTOP "DownloadStatPng: $2$\n$0"
- ${If} ${RunningX64}
- SetRegView 32
- ${EndIf}
+;
+; Pop $0 ; Get the parameter (file name to download)
+; ${If} ${RunningX64}
+; SetRegView 64
+; ${EndIf}
+; ReadRegStr $1 HKLM "SOFTWARE\Microsoft\Cryptography" "MachineGuid"
+; StrCpy $2 "https://www.sandboxie.com/img/$0?SessionId=$1"
+;
+; ;NSISdl::download_quiet /TIMEOUT=3000 $2 $TEMP\$0
+; inetc::get /SILENT /CONNECTTIMEOUT=5000 /RECEIVETIMEOUT=5000 $2 $TEMP\$0 /END
+; Pop $0 ;Get the return value
+; ;MessageBox MB_OK|MB_ICONSTOP "DownloadStatPng: $2$\n$0"
+; ${If} ${RunningX64}
+; SetRegView 32
+; ${EndIf}
FunctionEnd
diff --git a/Sandboxie/install/kmdutil/kmdutil.vcxproj b/Sandboxie/install/kmdutil/kmdutil.vcxproj
index c2bc00df..d94d736c 100644
--- a/Sandboxie/install/kmdutil/kmdutil.vcxproj
+++ b/Sandboxie/install/kmdutil/kmdutil.vcxproj
@@ -70,9 +70,7 @@
-
- $(WDKPATH)\lib\crt\amd64;$(LibraryPath)
-
+
diff --git a/SandboxiePlus/MiscHelpers/Common/CheckableMessageBox.cpp b/SandboxiePlus/MiscHelpers/Common/CheckableMessageBox.cpp
new file mode 100644
index 00000000..36ea785e
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/CheckableMessageBox.cpp
@@ -0,0 +1,235 @@
+#include "stdafx.h"
+#include "CheckableMessageBox.h"
+
+#include
+#include
+#include
+#include
+#include
+
+/*!
+ \class Utils::CCheckableMessageBox
+
+ \brief A messagebox suitable for questions with a
+ "Do not ask me again" checkbox.
+
+ Emulates the QMessageBox API with
+ static conveniences. The message label can open external URLs.
+*/
+
+
+class CCheckableMessageBoxPrivate
+{
+public:
+ CCheckableMessageBoxPrivate(QDialog *q)
+ : clickedButton(0)
+ {
+ QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
+
+ pixmapLabel = new QLabel(q);
+ sizePolicy.setHorizontalStretch(0);
+ sizePolicy.setVerticalStretch(0);
+ sizePolicy.setHeightForWidth(pixmapLabel->sizePolicy().hasHeightForWidth());
+ pixmapLabel->setSizePolicy(sizePolicy);
+ pixmapLabel->setVisible(false);
+
+ QSpacerItem *pixmapSpacer =
+ new QSpacerItem(0, 5, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding);
+
+ messageLabel = new QLabel(q);
+ messageLabel->setMinimumSize(QSize(300, 0));
+ messageLabel->setSizePolicy(QSizePolicy::Expanding, messageLabel->sizePolicy().verticalPolicy());
+ messageLabel->setWordWrap(true);
+ messageLabel->setOpenExternalLinks(true);
+ messageLabel->setTextInteractionFlags(Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse);
+
+ QSpacerItem *checkBoxRightSpacer =
+ new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum);
+ QSpacerItem *buttonSpacer =
+ new QSpacerItem(0, 1, QSizePolicy::Minimum, QSizePolicy::Minimum);
+
+ checkBox = new QCheckBox(q);
+ checkBox->setText(CCheckableMessageBox::tr("Do not ask again"));
+
+ buttonBox = new QDialogButtonBox(q);
+ buttonBox->setOrientation(Qt::Horizontal);
+ buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
+
+ QVBoxLayout *verticalLayout = new QVBoxLayout();
+ verticalLayout->addWidget(pixmapLabel);
+ verticalLayout->addItem(pixmapSpacer);
+
+ QHBoxLayout *horizontalLayout_2 = new QHBoxLayout();
+ horizontalLayout_2->addLayout(verticalLayout);
+ horizontalLayout_2->addWidget(messageLabel);
+
+ QHBoxLayout *horizontalLayout = new QHBoxLayout();
+ horizontalLayout->addWidget(checkBox);
+ horizontalLayout->addItem(checkBoxRightSpacer);
+
+ QVBoxLayout *verticalLayout_2 = new QVBoxLayout(q);
+ verticalLayout_2->addLayout(horizontalLayout_2);
+ verticalLayout_2->addLayout(horizontalLayout);
+ verticalLayout_2->addItem(buttonSpacer);
+ verticalLayout_2->addWidget(buttonBox);
+ }
+
+ QLabel *pixmapLabel;
+ QLabel *messageLabel;
+ QCheckBox *checkBox;
+ QDialogButtonBox *buttonBox;
+ QAbstractButton *clickedButton;
+};
+
+CCheckableMessageBox::CCheckableMessageBox(QWidget *parent) :
+ QDialog(parent),
+ d(new CCheckableMessageBoxPrivate(this))
+{
+ setModal(true);
+ setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
+ connect(d->buttonBox, SIGNAL(accepted()), SLOT(accept()));
+ connect(d->buttonBox, SIGNAL(rejected()), SLOT(reject()));
+ connect(d->buttonBox, SIGNAL(clicked(QAbstractButton*)),
+ SLOT(slotClicked(QAbstractButton*)));
+}
+
+CCheckableMessageBox::~CCheckableMessageBox()
+{
+ delete d;
+}
+
+void CCheckableMessageBox::slotClicked(QAbstractButton *b)
+{
+ d->clickedButton = b;
+}
+
+QAbstractButton *CCheckableMessageBox::clickedButton() const
+{
+ return d->clickedButton;
+}
+
+QDialogButtonBox::StandardButton CCheckableMessageBox::clickedStandardButton() const
+{
+ if (d->clickedButton)
+ return d->buttonBox->standardButton(d->clickedButton);
+ return QDialogButtonBox::NoButton;
+}
+
+QString CCheckableMessageBox::text() const
+{
+ return d->messageLabel->text();
+}
+
+void CCheckableMessageBox::setText(const QString &t)
+{
+ d->messageLabel->setText(t);
+}
+
+QPixmap CCheckableMessageBox::iconPixmap() const
+{
+ if (const QPixmap *p = d->pixmapLabel->pixmap())
+ return QPixmap(*p);
+ return QPixmap();
+}
+
+void CCheckableMessageBox::setIconPixmap(const QPixmap &p)
+{
+ d->pixmapLabel->setPixmap(p);
+ d->pixmapLabel->setVisible(!p.isNull());
+}
+
+bool CCheckableMessageBox::isChecked() const
+{
+ return d->checkBox->isChecked();
+}
+
+void CCheckableMessageBox::setChecked(bool s)
+{
+ d->checkBox->setChecked(s);
+}
+
+QString CCheckableMessageBox::checkBoxText() const
+{
+ return d->checkBox->text();
+}
+
+void CCheckableMessageBox::setCheckBoxText(const QString &t)
+{
+ d->checkBox->setText(t);
+}
+
+bool CCheckableMessageBox::isCheckBoxVisible() const
+{
+ return d->checkBox->isVisible();
+}
+
+void CCheckableMessageBox::setCheckBoxVisible(bool v)
+{
+ d->checkBox->setVisible(v);
+}
+
+QDialogButtonBox::StandardButtons CCheckableMessageBox::standardButtons() const
+{
+ return d->buttonBox->standardButtons();
+}
+
+void CCheckableMessageBox::setStandardButtons(QDialogButtonBox::StandardButtons s)
+{
+ d->buttonBox->setStandardButtons(s);
+}
+
+QPushButton *CCheckableMessageBox::button(QDialogButtonBox::StandardButton b) const
+{
+ return d->buttonBox->button(b);
+}
+
+QPushButton *CCheckableMessageBox::addButton(const QString &text, QDialogButtonBox::ButtonRole role)
+{
+ return d->buttonBox->addButton(text, role);
+}
+
+QDialogButtonBox::StandardButton CCheckableMessageBox::defaultButton() const
+{
+ foreach (QAbstractButton *b, d->buttonBox->buttons())
+ if (QPushButton *pb = qobject_cast(b))
+ if (pb->isDefault())
+ return d->buttonBox->standardButton(pb);
+ return QDialogButtonBox::NoButton;
+}
+
+void CCheckableMessageBox::setDefaultButton(QDialogButtonBox::StandardButton s)
+{
+ if (QPushButton *b = d->buttonBox->button(s)) {
+ b->setDefault(true);
+ b->setFocus();
+ }
+}
+
+QDialogButtonBox::StandardButton
+CCheckableMessageBox::question(QWidget *parent,
+ const QString &title,
+ const QString &question,
+ const QString &checkBoxText,
+ bool *checkBoxSetting,
+ QDialogButtonBox::StandardButtons buttons,
+ QDialogButtonBox::StandardButton defaultButton,
+ QMessageBox::Icon icon)
+{
+ CCheckableMessageBox mb(parent);
+ mb.setWindowTitle(title);
+ mb.setIconPixmap(QMessageBox::standardIcon(icon));
+ mb.setText(question);
+ mb.setCheckBoxText(checkBoxText);
+ mb.setChecked(*checkBoxSetting);
+ mb.setStandardButtons(buttons);
+ mb.setDefaultButton(defaultButton);
+ mb.exec();
+ *checkBoxSetting = mb.isChecked();
+ return mb.clickedStandardButton();
+}
+
+QMessageBox::StandardButton CCheckableMessageBox::dialogButtonBoxToMessageBoxButton(QDialogButtonBox::StandardButton db)
+{
+ return static_cast(int(db));
+}
+
diff --git a/SandboxiePlus/MiscHelpers/Common/CheckableMessageBox.h b/SandboxiePlus/MiscHelpers/Common/CheckableMessageBox.h
new file mode 100644
index 00000000..56026b8f
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/CheckableMessageBox.h
@@ -0,0 +1,75 @@
+#ifndef CHECKABLEMESSAGEBOX_H
+#define CHECKABLEMESSAGEBOX_H
+
+#include
+#include
+
+class CCheckableMessageBoxPrivate;
+
+#include "../mischelpers_global.h"
+
+class MISCHELPERS_EXPORT CCheckableMessageBox : public QDialog
+{
+ Q_OBJECT
+ Q_PROPERTY(QString text READ text WRITE setText)
+ Q_PROPERTY(QPixmap iconPixmap READ iconPixmap WRITE setIconPixmap)
+ Q_PROPERTY(bool isChecked READ isChecked WRITE setChecked)
+ Q_PROPERTY(QString checkBoxText READ checkBoxText WRITE setCheckBoxText)
+ Q_PROPERTY(QDialogButtonBox::StandardButtons buttons READ standardButtons WRITE setStandardButtons)
+ Q_PROPERTY(QDialogButtonBox::StandardButton defaultButton READ defaultButton WRITE setDefaultButton)
+
+public:
+ explicit CCheckableMessageBox(QWidget *parent = NULL);
+ virtual ~CCheckableMessageBox();
+
+ static QDialogButtonBox::StandardButton
+ question(QWidget *parent,
+ const QString &title,
+ const QString &question,
+ const QString &checkBoxText,
+ bool *checkBoxSetting,
+ QDialogButtonBox::StandardButtons buttons = QDialogButtonBox::Yes|QDialogButtonBox::No,
+ QDialogButtonBox::StandardButton defaultButton = QDialogButtonBox::No,
+ QMessageBox::Icon icon = QMessageBox::Question);
+
+ QString text() const;
+ void setText(const QString &);
+
+ bool isChecked() const;
+ void setChecked(bool s);
+
+ QString checkBoxText() const;
+ void setCheckBoxText(const QString &);
+
+ bool isCheckBoxVisible() const;
+ void setCheckBoxVisible(bool);
+
+ QDialogButtonBox::StandardButtons standardButtons() const;
+ void setStandardButtons(QDialogButtonBox::StandardButtons s);
+ QPushButton *button(QDialogButtonBox::StandardButton b) const;
+ QPushButton *addButton(const QString &text, QDialogButtonBox::ButtonRole role);
+
+ QDialogButtonBox::StandardButton defaultButton() const;
+ void setDefaultButton(QDialogButtonBox::StandardButton s);
+
+ // See static QMessageBox::standardPixmap()
+ QPixmap iconPixmap() const;
+ void setIconPixmap (const QPixmap &p);
+
+ // Query the result
+ QAbstractButton *clickedButton() const;
+ QDialogButtonBox::StandardButton clickedStandardButton() const;
+
+ // Conversion convenience
+ static QMessageBox::StandardButton dialogButtonBoxToMessageBoxButton(QDialogButtonBox::StandardButton);
+
+private slots:
+ void slotClicked(QAbstractButton *b);
+
+private:
+ CCheckableMessageBoxPrivate *d;
+};
+
+
+
+#endif // CHECKABLEMESSAGEBOX_H
diff --git a/SandboxiePlus/MiscHelpers/Common/ComboInputDialog.cpp b/SandboxiePlus/MiscHelpers/Common/ComboInputDialog.cpp
new file mode 100644
index 00000000..d90f3572
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/ComboInputDialog.cpp
@@ -0,0 +1,193 @@
+#include "stdafx.h"
+#include "ComboInputDialog.h"
+
+#include
+#include
+#include
+#include
+#include
+
+
+class CComboInputDialogPrivate
+{
+public:
+ CComboInputDialogPrivate(QDialog *q)
+ : clickedButton(0)
+ {
+ QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
+
+ pixmapLabel = new QLabel(q);
+ sizePolicy.setHorizontalStretch(0);
+ sizePolicy.setVerticalStretch(0);
+ sizePolicy.setHeightForWidth(pixmapLabel->sizePolicy().hasHeightForWidth());
+ pixmapLabel->setSizePolicy(sizePolicy);
+ pixmapLabel->setVisible(false);
+
+ QSpacerItem *pixmapSpacer =
+ new QSpacerItem(0, 5, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding);
+
+ messageLabel = new QLabel(q);
+ messageLabel->setMinimumSize(QSize(300, 0));
+ messageLabel->setWordWrap(true);
+ messageLabel->setOpenExternalLinks(true);
+ messageLabel->setTextInteractionFlags(Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse);
+
+ QSpacerItem *buttonSpacer =
+ new QSpacerItem(0, 1, QSizePolicy::Minimum, QSizePolicy::Minimum);
+
+ combo = new QComboBox(q);
+
+
+ buttonBox = new QDialogButtonBox(q);
+ buttonBox->setOrientation(Qt::Horizontal);
+ buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
+
+ QVBoxLayout *verticalLayout = new QVBoxLayout();
+ verticalLayout->addWidget(pixmapLabel);
+ verticalLayout->addItem(pixmapSpacer);
+
+ QHBoxLayout *horizontalLayout_2 = new QHBoxLayout();
+ horizontalLayout_2->addLayout(verticalLayout);
+ horizontalLayout_2->addWidget(messageLabel);
+
+ QVBoxLayout *verticalLayout_2 = new QVBoxLayout(q);
+ verticalLayout_2->addLayout(horizontalLayout_2);
+ verticalLayout_2->addWidget(combo);
+ verticalLayout_2->addItem(buttonSpacer);
+ verticalLayout_2->addWidget(buttonBox);
+ }
+
+ QLabel *pixmapLabel;
+ QLabel *messageLabel;
+ QComboBox* combo;
+ QDialogButtonBox *buttonBox;
+ QAbstractButton *clickedButton;
+};
+
+CComboInputDialog::CComboInputDialog(QWidget *parent) :
+ QDialog(parent),
+ d(new CComboInputDialogPrivate(this))
+{
+ setModal(true);
+ setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
+ connect(d->buttonBox, SIGNAL(accepted()), SLOT(accept()));
+ connect(d->buttonBox, SIGNAL(rejected()), SLOT(reject()));
+ connect(d->buttonBox, SIGNAL(clicked(QAbstractButton*)),
+ SLOT(slotClicked(QAbstractButton*)));
+}
+
+CComboInputDialog::~CComboInputDialog()
+{
+ delete d;
+}
+
+void CComboInputDialog::slotClicked(QAbstractButton *b)
+{
+ d->clickedButton = b;
+}
+
+QAbstractButton *CComboInputDialog::clickedButton() const
+{
+ return d->clickedButton;
+}
+
+QDialogButtonBox::StandardButton CComboInputDialog::clickedStandardButton() const
+{
+ if (d->clickedButton)
+ return d->buttonBox->standardButton(d->clickedButton);
+ return QDialogButtonBox::NoButton;
+}
+
+QString CComboInputDialog::text() const
+{
+ return d->messageLabel->text();
+}
+
+void CComboInputDialog::setText(const QString &t)
+{
+ d->messageLabel->setText(t);
+}
+
+void CComboInputDialog::addItem(const QString& t, const QVariant & v)
+{
+ d->combo->addItem(t, v);
+}
+
+void CComboInputDialog::setEditable(bool b)
+{
+ d->combo->setEditable(b);
+}
+
+QString CComboInputDialog::value() const
+{
+ return d->combo->currentText();
+}
+
+void CComboInputDialog::setValue(const QString &t)
+{
+ int idx = d->combo->findText(t);
+ if(idx == -1)
+ d->combo->setEditText(t);
+ else
+ d->combo->setCurrentIndex(idx);
+}
+
+QVariant CComboInputDialog::data() const
+{
+ return d->combo->currentData();
+}
+
+void CComboInputDialog::setData(const QVariant & v)
+{
+ d->combo->setCurrentIndex(d->combo->findData(v));
+}
+
+QPixmap CComboInputDialog::iconPixmap() const
+{
+ if (const QPixmap *p = d->pixmapLabel->pixmap())
+ return QPixmap(*p);
+ return QPixmap();
+}
+
+void CComboInputDialog::setIconPixmap(const QPixmap &p)
+{
+ d->pixmapLabel->setPixmap(p);
+ d->pixmapLabel->setVisible(!p.isNull());
+}
+
+QDialogButtonBox::StandardButtons CComboInputDialog::standardButtons() const
+{
+ return d->buttonBox->standardButtons();
+}
+
+void CComboInputDialog::setStandardButtons(QDialogButtonBox::StandardButtons s)
+{
+ d->buttonBox->setStandardButtons(s);
+}
+
+QPushButton *CComboInputDialog::button(QDialogButtonBox::StandardButton b) const
+{
+ return d->buttonBox->button(b);
+}
+
+QPushButton *CComboInputDialog::addButton(const QString &text, QDialogButtonBox::ButtonRole role)
+{
+ return d->buttonBox->addButton(text, role);
+}
+
+QDialogButtonBox::StandardButton CComboInputDialog::defaultButton() const
+{
+ foreach (QAbstractButton *b, d->buttonBox->buttons())
+ if (QPushButton *pb = qobject_cast(b))
+ if (pb->isDefault())
+ return d->buttonBox->standardButton(pb);
+ return QDialogButtonBox::NoButton;
+}
+
+void CComboInputDialog::setDefaultButton(QDialogButtonBox::StandardButton s)
+{
+ if (QPushButton *b = d->buttonBox->button(s)) {
+ b->setDefault(true);
+ b->setFocus();
+ }
+}
diff --git a/SandboxiePlus/MiscHelpers/Common/ComboInputDialog.h b/SandboxiePlus/MiscHelpers/Common/ComboInputDialog.h
new file mode 100644
index 00000000..bee2a394
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/ComboInputDialog.h
@@ -0,0 +1,61 @@
+#ifndef HCOMBOINPUTDIALOG_H
+#define HCOMBOINPUTDIALOG_H
+
+#include
+#include
+
+class CComboInputDialogPrivate;
+
+#include "../mischelpers_global.h"
+
+class MISCHELPERS_EXPORT CComboInputDialog: public QDialog
+{
+ Q_OBJECT
+ Q_PROPERTY(QString text READ text WRITE setText)
+ Q_PROPERTY(QPixmap iconPixmap READ iconPixmap WRITE setIconPixmap)
+ Q_PROPERTY(QDialogButtonBox::StandardButtons buttons READ standardButtons WRITE setStandardButtons)
+ Q_PROPERTY(QDialogButtonBox::StandardButton defaultButton READ defaultButton WRITE setDefaultButton)
+
+public:
+ explicit CComboInputDialog(QWidget *parent = NULL);
+ virtual ~CComboInputDialog();
+
+ QString text() const;
+ void setText(const QString &);
+
+ void addItem(const QString&, const QVariant & = QVariant());
+ void setEditable(bool);
+
+ QString value() const;
+ void setValue(const QString &);
+
+ QVariant data() const;
+ void setData(const QVariant &);
+
+
+ QDialogButtonBox::StandardButtons standardButtons() const;
+ void setStandardButtons(QDialogButtonBox::StandardButtons s);
+ QPushButton *button(QDialogButtonBox::StandardButton b) const;
+ QPushButton *addButton(const QString &text, QDialogButtonBox::ButtonRole role);
+
+ QDialogButtonBox::StandardButton defaultButton() const;
+ void setDefaultButton(QDialogButtonBox::StandardButton s);
+
+ // See static QMessageBox::standardPixmap()
+ QPixmap iconPixmap() const;
+ void setIconPixmap (const QPixmap &p);
+
+ // Query the result
+ QAbstractButton *clickedButton() const;
+ QDialogButtonBox::StandardButton clickedStandardButton() const;
+
+private slots:
+ void slotClicked(QAbstractButton *b);
+
+private:
+ CComboInputDialogPrivate *d;
+};
+
+
+
+#endif // HCOMBOINPUTDIALOG_H
diff --git a/SandboxiePlus/MiscHelpers/Common/Common.cpp b/SandboxiePlus/MiscHelpers/Common/Common.cpp
new file mode 100644
index 00000000..dfddd2c5
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/Common.cpp
@@ -0,0 +1,321 @@
+#include "stdafx.h"
+#include "Common.h"
+
+#ifdef USE_OPENSSL
+#include
+#endif
+
+#ifndef WIN32 // vswprintf
+#include
+#include
+
+
+int vswprintf_l(wchar_t * _String, size_t _Count, const wchar_t * _Format, va_list _Ap)
+{
+ wchar_t _Format_l[1025];
+ ASSERT(wcslen(_Format) < 1024);
+ wcscpy(_Format_l, _Format);
+
+ for(int i=0; igenerate64();
+#endif
+ return Rand64;
+}
+
+QString GetRand64Str(bool forUrl)
+{
+ quint64 Rand64 = GetRand64();
+ QString sRand64 = QByteArray((char*)&Rand64,sizeof(quint64)).toBase64();
+ if(forUrl)
+ sRand64.replace("+","-").replace("/","_");
+ return sRand64.replace("=","");
+}
+
+
+int GetRandomInt(int iMin, int iMax)
+{
+ return QRandomGenerator::system()->bounded(iMin, iMax);
+}
+
+StrPair Split2(const QString& String, QString Separator, bool Back)
+{
+ int Sep = Back ? String.lastIndexOf(Separator) : String.indexOf(Separator);
+ if(Sep != -1)
+ return qMakePair(String.left(Sep).trimmed(), String.mid(Sep+Separator.length()).trimmed());
+ return qMakePair(String.trimmed(), QString());
+}
+
+QStringList SplitStr(const QString& String, QString Separator)
+{
+ QStringList List = String.split(Separator);
+ for(int i=0; i < List.count(); i++)
+ {
+ List[i] = List[i].trimmed();
+ if(List[i].isEmpty())
+ List.removeAt(i--);
+ }
+ return List;
+}
+
+QString FormatSize(quint64 Size, int Precision)
+{
+ double Div;
+ if(Size > (quint64)(Div = 1.0*1024*1024*1024*1024*1024*1024))
+ return QString::number(double(Size)/Div, 'f', Precision) + " EB";
+ if(Size > (quint64)(Div = 1.0*1024*1024*1024*1024*1024))
+ return QString::number(double(Size)/Div, 'f', Precision) + " PB";
+ if(Size > (quint64)(Div = 1.0*1024*1024*1024*1024))
+ return QString::number(double(Size)/Div, 'f', Precision) + " TB";
+ if(Size > (quint64)(Div = 1.0*1024*1024*1024))
+ return QString::number(double(Size)/Div, 'f', Precision) + " GB";
+ if(Size > (quint64)(Div = 1.0*1024*1024))
+ return QString::number(double(Size)/Div, 'f', Precision) + " MB";
+ if(Size > (quint64)(Div = 1.0*1024))
+ return QString::number(double(Size)/Div, 'f', Precision) + " KB";
+ return QString::number(double(Size)) + "B";
+}
+
+QString FormatRate(quint64 Size, int Precision)
+{
+ return FormatSize(Size, Precision) + "/s";
+}
+
+QString FormatUnit(quint64 Size, int Precision)
+{
+ double Div;
+ if(Size > (quint64)(Div = 1.0*1000*1000*1000*1024*1000*1000))
+ return QString::number(double(Size)/Div, 'f', Precision) + " E";
+ if(Size > (quint64)(Div = 1.0*1000*1000*1000*1000*1000))
+ return QString::number(double(Size)/Div, 'f', Precision) + " P";
+ if(Size > (quint64)(Div = 1.0*1000*1000*1000*1000))
+ return QString::number(double(Size)/Div, 'f', Precision) + " T";
+ if(Size > (quint64)(Div = 1.0*1000*1000*1000))
+ return QString::number(double(Size)/Div, 'f', Precision) + " G";
+ if(Size > (quint64)(Div = 1.0*1000*1000))
+ return QString::number(double(Size)/Div, 'f', Precision) + " M";
+ if(Size > (quint64)(Div = 1.0*1000))
+ return QString::number(double(Size)/Div, 'f', Precision) + " K";
+ return QString::number(double(Size));
+}
+
+
+QString FormatTime(quint64 Time, bool ms)
+{
+ int miliseconds = 0;
+ if (ms) {
+ miliseconds = Time % 1000;
+ Time /= 1000;
+ }
+ int seconds = Time % 60;
+ Time /= 60;
+ int minutes = Time % 60;
+ Time /= 60;
+ int hours = Time % 24;
+ int days = Time / 24;
+ if(ms && (minutes == 0) && (hours == 0) && (days == 0))
+ return QString().sprintf("%02d.%04d", seconds, miliseconds);
+ if((hours == 0) && (days == 0))
+ return QString().sprintf("%02d:%02d", minutes, seconds);
+ if (days == 0)
+ return QString().sprintf("%02d:%02d:%02d", hours, minutes, seconds);
+ return QString().sprintf("%dd%02d:%02d:%02d", days, hours, minutes, seconds);
+}
+
+QString FormatNumber(quint64 Number)
+{
+ QString String = QString::number(Number);
+ for (int i = String.length() - 3; i > 0; i -= 3)
+ String.insert(i, QString::fromWCharArray(L"\u202F")); // L"\u2009"
+ return String;
+}
+
+QString FormatAddress(quint64 Address, int length)
+{
+ return "0x" + QString::number(Address, 16).rightJustified(length, '0');
+}
+
+bool ReadFromDevice(QIODevice* dev, char* data, int len, int timeout)
+{
+ while (dev->bytesAvailable() < len) {
+ if (!dev->waitForReadyRead(timeout))
+ return false;
+ }
+ return dev->read(data, len) == len;
+}
+
+
+void GrayScale (QImage& Image)
+{
+ if (Image.depth () == 32)
+ {
+ uchar* r = (Image.bits ());
+ uchar* g = (Image.bits () + 1);
+ uchar* b = (Image.bits () + 2);
+
+ uchar* end = (Image.bits() + Image.byteCount ());
+ while (r != end)
+ {
+ *r = *g = *b = (((*r + *g) >> 1) + *b) >> 1; // (r + b + g) / 3
+
+ r += 4;
+ g += 4;
+ b += 4;
+ }
+ }
+ else
+ {
+ for (int i = 0; i < Image.colorCount (); i++)
+ {
+ uint r = qRed (Image.color (i));
+ uint g = qGreen (Image.color (i));
+ uint b = qBlue (Image.color (i));
+
+ uint gray = (((r + g) >> 1) + b) >> 1;
+
+ Image.setColor (i, qRgba (gray, gray, gray, qAlpha (Image.color (i))));
+ }
+ }
+}
+
+QIcon MakeActionIcon(const QString& IconFile)
+{
+ QImage Image(IconFile);
+ QIcon Icon;
+ Icon.addPixmap(QPixmap::fromImage(Image), QIcon::Normal);
+ GrayScale(Image);
+ Icon.addPixmap(QPixmap::fromImage(Image), QIcon::Disabled);
+ return Icon;
+}
+
+QAction* MakeAction(QToolBar* pParent, const QString& IconFile, const QString& Text)
+{
+ QAction* pAction = new QAction(Text, pParent);
+ pAction->setIcon(MakeActionIcon(IconFile));
+ pParent->addAction(pAction);
+ return pAction;
+}
+
+QMenu* MakeMenu(QMenu* pParent, const QString& Text, const QString& IconFile)
+{
+ if(!IconFile.isEmpty())
+ {
+ QImage Image(IconFile);
+ QIcon Icon;
+ Icon.addPixmap(QPixmap::fromImage(Image), QIcon::Normal);
+ GrayScale(Image);
+ Icon.addPixmap(QPixmap::fromImage(Image), QIcon::Disabled);
+ return pParent->addMenu(Icon, Text);
+ }
+ return pParent->addMenu(Text);
+}
+
+QAction* MakeAction(QMenu* pParent, const QString& Text, const QString& IconFile)
+{
+ QAction* pAction = new QAction(Text, pParent);
+ if(!IconFile.isEmpty())
+ {
+ QImage Image(IconFile);
+ QIcon Icon;
+ Icon.addPixmap(QPixmap::fromImage(Image), QIcon::Normal);
+ GrayScale(Image);
+ Icon.addPixmap(QPixmap::fromImage(Image), QIcon::Disabled);
+ pAction->setIcon(Icon);
+ }
+ pParent->addAction(pAction);
+ return pAction;
+}
+
+QAction* MakeAction(QActionGroup* pGroup, QMenu* pParent, const QString& Text, const QVariant& Data)
+{
+ QAction* pAction = new QAction(Text, pParent);
+ pAction->setCheckable(true);
+ pAction->setData(Data);
+ pAction->setActionGroup(pGroup);
+ pParent->addAction(pAction);
+ return pAction;
+}
\ No newline at end of file
diff --git a/SandboxiePlus/MiscHelpers/Common/Common.h b/SandboxiePlus/MiscHelpers/Common/Common.h
new file mode 100644
index 00000000..3cbaae45
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/Common.h
@@ -0,0 +1,92 @@
+#pragma once
+
+#include "../mischelpers_global.h"
+
+#ifndef WIN32
+MISCHELPERS_EXPORT int vswprintf_l(wchar_t * _String, size_t _Count, const wchar_t * _Format, va_list _Ap);
+#endif
+
+MISCHELPERS_EXPORT time_t GetTime();
+MISCHELPERS_EXPORT quint64 GetCurTick();
+
+
+MISCHELPERS_EXPORT quint64 GetRand64();
+MISCHELPERS_EXPORT QString GetRand64Str(bool forUrl = true);
+
+MISCHELPERS_EXPORT int GetRandomInt(int iMin, int iMax);
+
+MISCHELPERS_EXPORT typedef QPair StrPair;
+MISCHELPERS_EXPORT StrPair Split2(const QString& String, QString Separator = "=", bool Back = false);
+MISCHELPERS_EXPORT QStringList SplitStr(const QString& String, QString Separator);
+
+MISCHELPERS_EXPORT QString UnEscape(QString Text);
+
+MISCHELPERS_EXPORT QString FormatSize(quint64 Size, int Precision = 2);
+__inline QString FormatSizeEx(quint64 Size, bool bEx) { return bEx && (Size == 0) ? QString() : FormatSize(Size); }
+MISCHELPERS_EXPORT QString FormatRate(quint64 Size, int Precision = 2);
+__inline QString FormatRateEx(quint64 Size, bool bEx) { return bEx && (Size == 0) ? QString() : FormatRate(Size); }
+MISCHELPERS_EXPORT QString FormatUnit(quint64 Size, int Precision = 0);
+MISCHELPERS_EXPORT QString FormatTime(quint64 Time, bool ms = false);
+MISCHELPERS_EXPORT QString FormatNumber(quint64 Number);
+__inline QString FormatNumberEx(quint64 Number, bool bEx) { return bEx && (Number == 0) ? QString() : FormatNumber(Number); }
+MISCHELPERS_EXPORT QString FormatAddress(quint64 Address, int length = 16);
+
+
+inline bool operator < (const QHostAddress &key1, const QHostAddress &key2)
+{
+ // Note: toIPv6Address also works for IPv4 addresses
+ Q_IPV6ADDR ip1 = key1.toIPv6Address();
+ Q_IPV6ADDR ip2 = key2.toIPv6Address();
+ return memcmp(&ip1, &ip2, sizeof(Q_IPV6ADDR)) < 0;
+}
+
+template
+QVariantList toVariantList( const QList &list )
+{
+ QVariantList newList;
+ foreach( const T &item, list )
+ newList << item;
+
+ return newList;
+}
+
+template
+QList reversed( const QList & in ) {
+ QList result;
+ result.reserve( in.size() ); // reserve is new in Qt 4.7
+ std::reverse_copy( in.begin(), in.end(), std::back_inserter( result ) );
+ return result;
+}
+
+template
+class CScoped
+{
+public:
+ CScoped(T* Val = NULL) {m_Val = Val;}
+ ~CScoped() {delete m_Val;}
+
+ CScoped& operator=(const CScoped& Scoped) {ASSERT(0); return *this;} // copying is explicitly forbidden
+ CScoped& operator=(T* Val) {ASSERT(!m_Val); m_Val = Val; return *this;}
+
+ inline T* Val() const {return m_Val;}
+ inline T* &Val() {return m_Val;}
+ inline T* Detache() {T* Val = m_Val; m_Val = NULL; return Val;}
+ inline T* operator->() const {return m_Val;}
+ inline T& operator*() const {return *m_Val;}
+ inline operator T*() const {return m_Val;}
+
+private:
+ T* m_Val;
+};
+
+MISCHELPERS_EXPORT bool ReadFromDevice(QIODevice* dev, char* data, int len, int timeout = 5000);
+
+
+MISCHELPERS_EXPORT void GrayScale (QImage& Image);
+
+MISCHELPERS_EXPORT QIcon MakeActionIcon(const QString& IconFile);
+MISCHELPERS_EXPORT QAction* MakeAction(QToolBar* pParent, const QString& IconFile, const QString& Text = "");
+MISCHELPERS_EXPORT QMenu* MakeMenu(QMenu* pParent, const QString& Text, const QString& IconFile = "");
+MISCHELPERS_EXPORT QAction* MakeAction(QMenu* pParent, const QString& Text, const QString& IconFile = "");
+MISCHELPERS_EXPORT QAction* MakeAction(QActionGroup* pGroup, QMenu* pParent, const QString& Text, const QVariant& Data);
+
diff --git a/SandboxiePlus/MiscHelpers/Common/DebugHelpers.cpp b/SandboxiePlus/MiscHelpers/Common/DebugHelpers.cpp
new file mode 100644
index 00000000..757cc73d
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/DebugHelpers.cpp
@@ -0,0 +1,391 @@
+#include "stdafx.h"
+#include "DebugHelpers.h"
+#include "Common.h"
+#include
+
+#ifdef WIN32
+#include
+#endif
+
+bool IsDebuggerAttached()
+{
+ bool isDebuggerPresent = false; // Note: on linux change edit the value in debgger to indicate precense
+#ifdef WIN32
+ if (IsDebuggerPresent())
+ return true;
+#endif
+ return isDebuggerPresent;
+}
+
+void WaitForDebugger()
+{
+ while (!IsDebuggerAttached())
+ QThread::msleep(500);
+}
+
+
+#if defined(_DEBUG) || defined(_TRACE)
+
+#ifdef WIN32
+bool g_assert_active = IsDebuggerPresent();
+#else
+bool g_assert_active = true;
+#endif
+
+void Assert(bool test)
+{
+ if(!test && g_assert_active)
+ _CrtDbgBreak();
+}
+
+void CTracer::operator()(const QString &sLine) const
+{
+ qDebug() << sLine;
+}
+
+void CTracer::operator()(const wchar_t *sLine, ...) const
+{
+ const size_t bufferSize = 10241;
+ wchar_t bufferline[bufferSize];
+
+ va_list argptr;
+ va_start(argptr, sLine);
+#ifndef WIN32
+ if (vswprintf_l(bufferline, bufferSize, sLine, argptr) == -1)
+#else
+ if (vswprintf(bufferline, bufferSize, sLine, argptr) == -1)
+#endif
+ bufferline[bufferSize - 1] = L'\0';
+ va_end(argptr);
+
+ QString sBufferLine = QString::fromWCharArray(bufferline);
+ qDebug() << sBufferLine;
+}
+
+void CTracer::operator()(const char *sLine, ...) const
+{
+ const size_t bufferSize = 10241;
+ char bufferline[bufferSize];
+
+ va_list argptr;
+ va_start(argptr, sLine);
+ if (vsprintf(bufferline, sLine, argptr) == -1)
+ bufferline[bufferSize - 1] = L'\0';
+ va_end(argptr);
+
+ QString sBufferLine = bufferline;
+ qDebug() << sBufferLine;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////
+// Tracers
+////////////////////////////////////////////////////////////////////////////////////////////
+
+CMemTracer memTracer;
+
+void CMemTracer::DumpTrace()
+{
+ QMutexLocker Locker(&m_Locker);
+ for(map::iterator I = m_MemoryTrace.begin(); I != m_MemoryTrace.end(); I++)
+ {
+
+ map::iterator J = m_MemoryTrace2.find(I->first);
+ if(J != m_MemoryTrace2.end())
+ TRACE(L"MEMORY TRACE: Object '%S' has %d (%d) instances.",I->first.c_str(),I->second, J->second);
+ else
+ TRACE(L"MEMORY TRACE: Object '%S' has %d instances.",I->first.c_str(),I->second);
+ }
+}
+
+void CMemTracer::TraceAlloc(string Name)
+{
+ QMutexLocker Locker(&m_Locker);
+ m_MemoryTrace[Name] += 1;
+}
+
+void CMemTracer::TraceFree(string Name)
+{
+ QMutexLocker Locker(&m_Locker);
+ m_MemoryTrace[Name] -= 1;
+
+ //if(m_MemoryTrace2[Name] > 0)
+ if(m_MemoryTrace2.find(Name) != m_MemoryTrace2.end())
+ m_MemoryTrace2[Name] -= 1;
+}
+
+void CMemTracer::TracePre(string Name)
+{
+ QMutexLocker Locker(&m_Locker);
+ m_MemoryTrace2[Name] += 1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+CCpuTracer cpuTracer;
+
+void CCpuTracer::DumpTrace()
+{
+ QMutexLocker Locker(&m_Locker);
+ for(map::iterator I = m_CpuUsageTrace.begin(); I != m_CpuUsageTrace.end(); I++)
+ TRACE(L"CPU TRACE: Prozedure '%S' needed %f seconds.",I->first.c_str(),(double)I->second.Total/1000000.0);
+}
+
+void CCpuTracer::ResetTrace()
+{
+ QMutexLocker Locker(&m_Locker);
+ m_CpuUsageTrace.clear();
+}
+
+void CCpuTracer::TraceStart(string Name)
+{
+ QMutexLocker Locker(&m_Locker);
+ m_CpuUsageTrace[Name].Counting = GetCurCycle();
+}
+
+void CCpuTracer::TraceStop(string Name)
+{
+ QMutexLocker Locker(&m_Locker);
+ m_CpuUsageTrace[Name].Total += GetCurCycle() - m_CpuUsageTrace[Name].Counting;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////////////
+CLockTracer lockTracer;
+
+void CLockTracer::DumpTrace()
+{
+ QMutexLocker Locker(&m_Locker);
+ for(map::iterator I = m_LockTrace.begin(); I != m_LockTrace.end(); I++)
+ TRACE(L"LOCK TRACE: Lock '%S' has %d Locks for %f seconds.",I->first.c_str(),I->second.LockCount, (double)(GetCurCycle()/1000 - I->second.LockTime) / 1000);
+}
+
+void CLockTracer::TraceLock(string Name, int op)
+{
+ QMutexLocker Locker(&m_Locker);
+ SLocks &Locks = m_LockTrace[Name];
+ if(Locks.LockCount == 0)
+ Locks.LockTime = GetCurCycle()/1000;
+ Locks.LockCount += op;
+ if(Locks.LockCount == 0)
+ Locks.LockTime = 0;
+}
+#endif
+
+
+/////////////////////////////////////////////////////////////////////////////////////////////
+//
+
+#ifdef WIN32
+#include
+
+typedef BOOL (__stdcall *tMDWD)(
+ IN HANDLE hProcess,
+ IN DWORD ProcessId,
+ IN HANDLE hFile,
+ IN MINIDUMP_TYPE DumpType,
+ IN CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, OPTIONAL
+ IN CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, OPTIONAL
+ IN CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam OPTIONAL
+ );
+
+
+static tMDWD s_pMDWD;
+static HMODULE s_hDbgHelpMod;
+static MINIDUMP_TYPE s_dumpTyp = MiniDumpNormal; // MiniDumpWithDataSegs or MiniDumpWithFullMemory
+static wchar_t s_szMiniDumpName[64];
+
+static LONG __stdcall MyCrashHandlerExceptionFilter(EXCEPTION_POINTERS* pEx)
+{
+#ifdef _M_IX86
+ if (pEx->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW)
+ {
+ // be sure that we have enought space...
+ static char MyStack[1024*128];
+ // it assumes that DS and SS are the same!!! (this is the case for Win32)
+ // change the stack only if the selectors are the same (this is the case for Win32)
+ //__asm push offset MyStack[1024*128];
+ //__asm pop esp;
+ __asm mov eax,offset MyStack[1024*128];
+ __asm mov esp,eax;
+ }
+#endif
+ bool bSuccess = false;
+
+ wchar_t szMiniDumpFileName[128];
+ wsprintf(szMiniDumpFileName, L"%s %s.dmp", s_szMiniDumpName, QDateTime::currentDateTime().toString("dd.MM.yyyy hh-mm-ss,zzz").replace(QRegExp("[:*?<>|\"\\/]"), "_").toStdWString().c_str());
+
+ wchar_t szMiniDumpPath[MAX_PATH] = { 0 };
+
+ HANDLE hFile = CreateFile(szMiniDumpFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hFile != INVALID_HANDLE_VALUE)
+ GetCurrentDirectory(MAX_PATH, szMiniDumpPath);
+ else
+ {
+ GetTempPath(MAX_PATH, szMiniDumpPath);
+
+ wchar_t szMiniDumpFilePath[MAX_PATH] = { 0 };
+ wsprintf(szMiniDumpFilePath, L"%s\\%s.dmp", szMiniDumpPath, szMiniDumpFileName);
+ hFile = CreateFile(szMiniDumpFilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ }
+
+ if (hFile != INVALID_HANDLE_VALUE)
+ {
+ MINIDUMP_EXCEPTION_INFORMATION stMDEI;
+ stMDEI.ThreadId = GetCurrentThreadId();
+ stMDEI.ExceptionPointers = pEx;
+ stMDEI.ClientPointers = TRUE;
+ // try to create an miniDump:
+ if (s_pMDWD(GetCurrentProcess(), GetCurrentProcessId(), hFile, s_dumpTyp, &stMDEI, NULL, NULL))
+ {
+ bSuccess = true;
+ }
+ CloseHandle(hFile);
+ }
+
+ wchar_t szMiniDumpMessage[256];
+ if (!bSuccess)
+ wsprintf(szMiniDumpMessage, L"%s crashed!\r\nCrashdump creation failed.", s_szMiniDumpName);
+ else
+ wsprintf(szMiniDumpMessage, L"%s crashed!\r\nCrashdump saved to \"%s\".\r\nPlease report the crash and attach the file \"%s\".", s_szMiniDumpName, szMiniDumpPath, szMiniDumpFileName);
+ MessageBox(NULL, szMiniDumpMessage, s_szMiniDumpName, MB_OK | MB_ICONERROR);
+
+ // or return one of the following:
+ // - EXCEPTION_CONTINUE_SEARCH
+ // - EXCEPTION_CONTINUE_EXECUTION
+ // - EXCEPTION_EXECUTE_HANDLER
+ return EXCEPTION_CONTINUE_SEARCH; // this will trigger the "normal" OS error-dialog
+}
+
+void InitMiniDumpWriter(const wchar_t* Name)
+{
+ if (s_hDbgHelpMod != NULL)
+ return;
+
+ ASSERT(wcslen(Name) < ARRSIZE(s_szMiniDumpName));
+ wcscpy(s_szMiniDumpName, Name);
+
+ // Initialize the member, so we do not load the dll after the exception has occured
+ // which might be not possible anymore...
+ s_hDbgHelpMod = LoadLibrary(L"dbghelp.dll");
+ if (s_hDbgHelpMod != NULL)
+ s_pMDWD = (tMDWD) GetProcAddress(s_hDbgHelpMod, "MiniDumpWriteDump");
+
+ // Register Unhandled Exception-Filter:
+ SetUnhandledExceptionFilter(MyCrashHandlerExceptionFilter);
+
+ // Additional call "PreventSetUnhandledExceptionFilter"...
+ // See also: "SetUnhandledExceptionFilter" and VC8 (and later)
+ // http://blog.kalmbachnet.de/?postid=75
+}
+
+
+quint64 GetCurCycle()
+{
+ quint64 freq, now;
+ QueryPerformanceFrequency((LARGE_INTEGER*)&freq);
+ QueryPerformanceCounter((LARGE_INTEGER*)&now);
+ quint64 dwNow = ((now * 1000000) / freq) & 0xffffffff;
+ return dwNow; // returns time since system start in us
+}
+
+
+#elif !defined(__APPLE__)
+#include
+#include
+#include
+#include
+#include
+
+static wchar_t s_szMiniDumpName[64];
+
+/* This structure mirrors the one found in /usr/include/asm/ucontext.h */
+typedef struct _sig_ucontext {
+ unsigned long uc_flags;
+ struct ucontext *uc_link;
+ stack_t uc_stack;
+ struct sigcontext uc_mcontext;
+ sigset_t uc_sigmask;
+} sig_ucontext_t;
+
+void crit_err_hdlr(int sig_num, siginfo_t * info, void * ucontext)
+{
+ void * array[50];
+ void * caller_address;
+ char ** messages;
+ int size, i;
+ sig_ucontext_t * uc;
+
+ uc = (sig_ucontext_t *)ucontext;
+
+ /* Get the address at the time the signal was raised */
+#if defined(__i386__) // gcc specific
+ caller_address = (void *) uc->uc_mcontext.eip; // EIP: x86 specific
+#elif defined(__x86_64__) // gcc specific
+ caller_address = (void *) uc->uc_mcontext.rip; // RIP: x86_64 specific
+#else
+#error Unsupported architecture. // TO-DO: Add support for other arch.
+#endif
+
+ fprintf(stderr, "signal %d (%s), address is %p from %p\n",
+ sig_num, strsignal(sig_num), info->si_addr,
+ (void *)caller_address);
+
+ size = backtrace(array, 50);
+
+ /* overwrite sigaction with caller's address */
+ array[1] = caller_address;
+
+ messages = backtrace_symbols(array, size);
+
+ char szMiniDumpFileName[128];
+ sprintf(szMiniDumpFileName, "%S_%s.log", s_szMiniDumpName, QDateTime::currentDateTime().toString("dd.MM.yyyy_hh-mm-ss,zzz").replace(QRegExp("[:*?<>|\"\\/]"), "_").toStdString().c_str());
+
+ FILE* file = fopen(szMiniDumpFileName, "wb");
+
+ /* skip first stack frame (points here) */
+ for (i = 1; i < size && messages != NULL; ++i)
+ {
+ fprintf(stderr, "[bt]: (%d) %s\n", i, messages[i]);
+ fprintf(file, "[bt]: (%d) %s\n", i, messages[i]);
+ }
+
+ fclose(file);
+
+ free(messages);
+
+ exit(EXIT_FAILURE);
+}
+
+void InstallSigAction(int sig)
+{
+ struct sigaction sigact;
+ sigact.sa_sigaction = crit_err_hdlr;
+ sigact.sa_flags = SA_RESTART | SA_SIGINFO;
+
+ if (sigaction(sig, &sigact, (struct sigaction *)NULL) != 0)
+ {
+ fprintf(stderr, "error setting signal handler for %d (%s)\n", sig, strsignal(sig));
+ exit(EXIT_FAILURE);
+ }
+}
+
+void InitMiniDumpWriter(const wchar_t* Name)
+{
+ ASSERT(wcslen(Name) < ARRSIZE(s_szMiniDumpName));
+ wcscpy(s_szMiniDumpName, Name);
+
+ InstallSigAction(SIGABRT);
+ InstallSigAction(SIGSEGV);
+}
+
+quint64 GetCurCycle()
+{
+ return GetCurTick()*1000; // ToDo
+}
+
+#else
+void InitMiniDumpWriter(const wchar_t* Name)
+{
+
+}
+#endif
diff --git a/SandboxiePlus/MiscHelpers/Common/DebugHelpers.h b/SandboxiePlus/MiscHelpers/Common/DebugHelpers.h
new file mode 100644
index 00000000..c301199c
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/DebugHelpers.h
@@ -0,0 +1,173 @@
+#pragma once
+
+//#define _TRACE
+#include "../mischelpers_global.h"
+
+MISCHELPERS_EXPORT bool IsDebuggerAttached();
+
+MISCHELPERS_EXPORT void WaitForDebugger();
+
+#if defined(_DEBUG) || defined(_TRACE)
+
+
+MISCHELPERS_EXPORT void Assert(bool test);
+
+ #define TRACE CTracer()
+ #define ASSERT(x) Assert(x)
+ #define VERIFY(f) ASSERT(f)
+ #define DEBUG_ONLY(f) (f)
+
+MISCHELPERS_EXPORT extern bool g_assert_active;
+
+class MISCHELPERS_EXPORT CTracer
+{
+public:
+ void operator()(const QString &sLine) const;
+ void operator()(const wchar_t *sLine, ...) const;
+ void operator()(const char *sLine, ...) const;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////
+// Tracers
+////////////////////////////////////////////////////////////////////////////////////////////
+
+class MISCHELPERS_EXPORT CMemTracer
+{
+public:
+ //CMemTracer();
+ //~CMemTracer();
+
+ void DumpTrace();
+
+ void TraceAlloc(string Name);
+ void TraceFree(string Name);
+ void TracePre(string Name);
+
+
+private:
+ QMutex m_Locker;
+ map m_MemoryTrace;
+ map m_MemoryTrace2;
+};
+
+MISCHELPERS_EXPORT extern CMemTracer memTracer;
+
+ #define TRACE_ALLOC(x) memTracer.TraceAlloc(x);
+ #define TRACE_FREE(x) memTracer.TraceFree(x);
+ #define TRACE_PRE_FREE(x) memTracer.TracePre(x);
+ #define TRACE_MEMORY memTracer.DumpTrace();
+
+class MISCHELPERS_EXPORT CCpuTracer
+{
+public:
+ //CCpuTracer();
+ //~CCpuTracer();
+
+ void DumpTrace();
+ void ResetTrace();
+
+ void TraceStart(string Name);
+ void TraceStop(string Name);
+
+private:
+ struct SCycles
+ {
+ SCycles()
+ {
+ Counting = 0;
+ Total = 0;
+ }
+ quint64 Counting;
+ quint64 Total;
+ };
+ QMutex m_Locker;
+ map m_CpuUsageTrace;
+};
+
+MISCHELPERS_EXPORT extern CCpuTracer cpuTracer;
+
+ #define TRACE_RESET cpuTracer.ResetTrace();
+ #define TRACE_START(x) cpuTracer.TraceStart(x);
+ #define TRACE_STOP(x) cpuTracer.TraceStop(x);
+ #define TRACE_CPU cpuTracer.DumpTrace();
+
+
+class MISCHELPERS_EXPORT CLockTracer
+{
+public:
+ //CLockTracer();
+ //~CLockTracer();
+
+ void DumpTrace();
+
+ void TraceLock(string Name, int op);
+
+private:
+ QMutex m_Locker;
+ struct SLocks
+ {
+ SLocks()
+ {
+ LockTime = 0;
+ LockCount = 0;
+ }
+ time_t LockTime;
+ int LockCount;
+ };
+ map m_LockTrace;
+};
+
+MISCHELPERS_EXPORT extern CLockTracer lockTracer;
+
+ #define TRACE_LOCK(x) lockTracer.TraceLock(x,1);
+ #define TRACE_UNLOCK(x)lockTracer.TraceLock(x,-1);
+ #define TRACE_LOCKING lockTracer.DumpTrace();
+
+class MISCHELPERS_EXPORT CLockerTrace
+{
+public:
+ CLockerTrace(string Name)
+ {
+ m_Name = Name;
+ lockTracer.TraceLock(m_Name,1);
+ }
+ ~CLockerTrace()
+ {
+ lockTracer.TraceLock(m_Name,-1);
+ }
+
+private:
+ string m_Name;
+};
+
+ #define TRACE_LOCKER(x)CLockerTrace LockTrace(x)
+
+
+
+#else
+
+//#define X_ASSERT(x) if(!(x)) throw "Assertion failed in: " __FUNCTION__ "; File:" __FILE__ "; Line:" STR(__LINE__) ";";
+
+ #define TRACE
+ #define ASSERT(x)
+ #define VERIFY(f) (f)
+ #define DEBUG_ONLY(f)
+
+ #define TRACE_ALLOC(x)
+ #define TRACE_FREE(x)
+ #define TRACE_MEMORY
+
+ #define TRACE_RESET
+ #define TRACE_START(x)
+ #define TRACE_STOP(x)
+ #define TRACE_CPUUSAGE
+ #define TRACE_CPU
+
+ #define TRACE_LOCK(x)
+ #define TRACE_UNLOCK(x)
+ #define TRACE_LOCKING
+ #define TRACE_LOCKER(x)
+#endif
+
+MISCHELPERS_EXPORT void InitMiniDumpWriter(const wchar_t* Name);
+MISCHELPERS_EXPORT quint64 GetCurCycle();
\ No newline at end of file
diff --git a/SandboxiePlus/MiscHelpers/Common/ExitDialog.h b/SandboxiePlus/MiscHelpers/Common/ExitDialog.h
new file mode 100644
index 00000000..85c0973d
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/ExitDialog.h
@@ -0,0 +1,66 @@
+#pragma once
+
+#include "../mischelpers_global.h"
+
+class MISCHELPERS_EXPORT CExitDialog : public QDialog
+{
+public:
+ CExitDialog(const QString& Prompt, QWidget* parent = 0)
+ : QDialog(parent)
+ {
+ m_pMainLayout = new QGridLayout(this);
+
+ QLabel* pLabel = new QLabel(Prompt);
+ m_pMainLayout->addWidget(pLabel, 0, 0, 1, 1);
+
+ m_pButtonBox = new QDialogButtonBox();
+ m_pButtonBox->setOrientation(Qt::Horizontal);
+ m_pButtonBox->setStandardButtons(QDialogButtonBox::Yes|QDialogButtonBox::No);
+ m_pMainLayout->addWidget(m_pButtonBox, 2, 0, 1, 1);
+
+ connect(m_pButtonBox,SIGNAL(accepted()),this,SLOT(accept()));
+ connect(m_pButtonBox,SIGNAL(rejected()),this,SLOT(reject()));
+
+ m_TimerId = startTimer(1000);
+ m_CountDown = 15;
+ }
+ ~CExitDialog()
+ {
+ killTimer(m_TimerId);
+ }
+
+protected:
+ void timerEvent(QTimerEvent *e)
+ {
+ if (e->timerId() != m_TimerId)
+ {
+ QDialog::timerEvent(e);
+ return;
+ }
+
+ if(m_CountDown != 0)
+ {
+ m_CountDown--;
+ m_pButtonBox->button(QDialogButtonBox::Yes)->setText(tr("Yes (%1)").arg(m_CountDown));
+ if(m_CountDown == 0)
+ accept();
+ }
+ }
+
+ void reject()
+ {
+ hide();
+ }
+
+ void closeEvent(QCloseEvent *e)
+ {
+ hide();
+ e->ignore();
+ }
+
+ int m_TimerId;
+ int m_CountDown;
+
+ QGridLayout* m_pMainLayout;
+ QDialogButtonBox * m_pButtonBox;
+};
diff --git a/SandboxiePlus/MiscHelpers/Common/Finder.cpp b/SandboxiePlus/MiscHelpers/Common/Finder.cpp
new file mode 100644
index 00000000..bb9c97e7
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/Finder.cpp
@@ -0,0 +1,125 @@
+#include "stdafx.h"
+#include "Finder.h"
+
+QWidget* CFinder::AddFinder(QWidget* pList, QObject* pFilterTarget, bool HighLightOption, CFinder** ppFinder)
+{
+ QWidget* pWidget = new QWidget();
+ QVBoxLayout* pLayout = new QVBoxLayout();
+ pLayout->setMargin(0);
+ pWidget->setLayout(pLayout);
+
+ pLayout->addWidget(pList);
+ CFinder* pFinder = new CFinder(pFilterTarget, pWidget, HighLightOption);
+ pLayout->addWidget(pFinder);
+
+ if (ppFinder)
+ *ppFinder = pFinder;
+ return pWidget;
+}
+
+CFinder::CFinder(QObject* pFilterTarget, QWidget *parent, bool HighLightOption)
+:QWidget(parent)
+{
+ m_pSearchLayout = new QHBoxLayout();
+ m_pSearchLayout->setMargin(3);
+ m_pSearchLayout->setSpacing(3);
+ m_pSearchLayout->setAlignment(Qt::AlignLeft);
+
+ m_pSearch = new QLineEdit();
+ m_pSearch->setMinimumWidth(150);
+ m_pSearch->setMaximumWidth(350);
+ m_pSearchLayout->addWidget(m_pSearch);
+ QObject::connect(m_pSearch, SIGNAL(textChanged(QString)), this, SLOT(OnUpdate()));
+ //QObject::connect(m_pSearch, SIGNAL(returnPressed()), this, SLOT(_q_next()));
+
+ m_pCaseSensitive = new QCheckBox(tr("Case Sensitive"));
+ m_pSearchLayout->addWidget(m_pCaseSensitive);
+ connect(m_pCaseSensitive, SIGNAL(stateChanged(int)), this, SLOT(OnUpdate()));
+
+ m_pRegExp = new QCheckBox(tr("RegExp"));
+ m_pSearchLayout->addWidget(m_pRegExp);
+ connect(m_pRegExp, SIGNAL(stateChanged(int)), this, SLOT(OnUpdate()));
+
+ m_pColumn = new QComboBox();
+ m_pSearchLayout->addWidget(m_pColumn);
+ connect(m_pColumn, SIGNAL(currentIndexChanged(int)), this, SLOT(OnUpdate()));
+ m_pColumn->setVisible(false);
+
+ if (HighLightOption)
+ {
+ m_pHighLight = new QCheckBox(tr("Highlight"));
+ m_pSearchLayout->addWidget(m_pHighLight);
+ connect(m_pHighLight, SIGNAL(stateChanged(int)), this, SLOT(OnUpdate()));
+ }
+ else
+ m_pHighLight = NULL;
+
+ QToolButton* pClose = new QToolButton(this);
+ pClose->setIcon(QIcon(":/close.png"));
+ pClose->setAutoRaise(true);
+ pClose->setText(tr("Close"));
+ m_pSearchLayout->addWidget(pClose);
+ QObject::connect(pClose, SIGNAL(clicked()), this, SLOT(Close()));
+
+ QWidget* pSpacer = new QWidget();
+ pSpacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ m_pSearchLayout->addWidget(pSpacer);
+
+ setLayout(m_pSearchLayout);
+
+ setMaximumHeight(30);
+
+ hide();
+
+ if (parent)
+ {
+ QAction* pFind = new QAction(tr("&Find ..."), parent);
+ pFind->setShortcut(QKeySequence::Find);
+ pFind->setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ parent->addAction(pFind);
+ QObject::connect(pFind, SIGNAL(triggered()), this, SLOT(Open()));
+ }
+
+ m_pSortProxy = qobject_cast(pFilterTarget);
+ if (pFilterTarget)
+ QObject::connect(this, SIGNAL(SetFilter(const QRegExp&, bool, int)), pFilterTarget, SLOT(SetFilter(const QRegExp&, bool, int)));
+}
+
+CFinder::~CFinder()
+{
+}
+
+
+void CFinder::Open()
+{
+ if (m_pSortProxy && m_pColumn->count() == 0)
+ {
+ m_pColumn->addItem(tr("All columns"), -1);
+ for (int i = 0; i < m_pSortProxy->columnCount(); i++)
+ m_pColumn->addItem(m_pSortProxy->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString(), i);
+ m_pColumn->setVisible(true);
+ }
+
+ show();
+ m_pSearch->setFocus(Qt::OtherFocusReason);
+ m_pSearch->selectAll();
+ OnUpdate();
+}
+
+QRegExp CFinder::GetRegExp() const
+{
+ if (!isVisible())
+ return QRegExp();
+ return QRegExp(m_pSearch->text(), m_pCaseSensitive->isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive, m_pRegExp->isChecked() ? QRegExp::RegExp : QRegExp::FixedString);
+}
+
+void CFinder::OnUpdate()
+{
+ emit SetFilter(GetRegExp(), GetHighLight(), GetColumn());
+}
+
+void CFinder::Close()
+{
+ emit SetFilter(QRegExp());
+ hide();
+}
\ No newline at end of file
diff --git a/SandboxiePlus/MiscHelpers/Common/Finder.h b/SandboxiePlus/MiscHelpers/Common/Finder.h
new file mode 100644
index 00000000..b4fc799b
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/Finder.h
@@ -0,0 +1,38 @@
+#pragma once
+
+#include "../mischelpers_global.h"
+
+class MISCHELPERS_EXPORT CFinder: public QWidget
+{
+ Q_OBJECT
+
+public:
+ CFinder(QObject* pFilterTarget, QWidget *parent = NULL, bool HighLightOption = true);
+ ~CFinder();
+
+ static QWidget* AddFinder(QWidget* pList, QObject* pFilterTarget, bool HighLightOption = true, CFinder** ppFinder = NULL);
+
+ QRegExp GetRegExp() const;
+ bool GetHighLight() const { return m_pHighLight ? m_pHighLight->isChecked() : false; }
+ int GetColumn() const { return m_pColumn->currentData().toInt(); }
+
+signals:
+ void SetFilter(const QRegExp& Exp, bool bHighLight = false, int Column = -1);
+
+public slots:
+ void Open();
+ void OnUpdate();
+ void Close();
+
+private:
+
+ QHBoxLayout* m_pSearchLayout;
+
+ QLineEdit* m_pSearch;
+ QCheckBox* m_pCaseSensitive;
+ QCheckBox* m_pRegExp;
+ QComboBox* m_pColumn;
+ QCheckBox* m_pHighLight;
+
+ QSortFilterProxyModel* m_pSortProxy;
+};
\ No newline at end of file
diff --git a/SandboxiePlus/MiscHelpers/Common/FlexError.h b/SandboxiePlus/MiscHelpers/Common/FlexError.h
new file mode 100644
index 00000000..08ed25a6
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/FlexError.h
@@ -0,0 +1,87 @@
+#pragma once
+
+#define ERROR_UNDEFINED (1)
+#define ERROR_CONFIRM (2)
+#define ERROR_INTERNAL (3)
+#define ERROR_PARAMS (4)
+
+#include "../mischelpers_global.h"
+
+class MISCHELPERS_EXPORT CFlexError
+{
+public:
+ CFlexError()
+ {
+ m = NULL;
+ }
+ CFlexError(const QString& Error, long Status = ERROR_UNDEFINED) : CFlexError()
+ {
+ Attach(MkError(Error, Status));
+ }
+ CFlexError(long Status) : CFlexError(QObject::tr("Error"), Status)
+ {
+ }
+ CFlexError(const CFlexError& other) : CFlexError()
+ {
+ if(other.m != NULL)
+ Attach((SFlexError*)other.m);
+ }
+ ~CFlexError()
+ {
+ Detach();
+ }
+
+ CFlexError& operator=(const CFlexError& Array)
+ {
+ Attach(Array.m);
+ return *this;
+ }
+
+ __inline bool IsError() const { return m != NULL; }
+ __inline long GetStatus() const { return m ? m->Status : 0; }
+ __inline QString GetText() const { return m ? m->Error: ""; }
+
+ operator bool() const {return !IsError();}
+
+private:
+ struct SFlexError
+ {
+ QString Error;
+ long Status;
+
+ mutable atomic aRefCnt;
+ } *m;
+
+ SFlexError* MkError(const QString& Error, long Status)
+ {
+ SFlexError* p = new SFlexError();
+ p->Error = Error;
+ p->Status = Status;
+ return p;
+ }
+
+ void Attach(SFlexError* p)
+ {
+ Detach();
+
+ if (p != NULL)
+ {
+ p->aRefCnt.fetch_add(1);
+ m = p;
+ }
+ }
+
+ void Detach()
+ {
+ if (m != NULL)
+ {
+ if (m->aRefCnt.fetch_sub(1) == 1)
+ delete m;
+ m = NULL;
+ }
+ }
+};
+
+typedef CFlexError STATUS;
+#define OK CFlexError()
+#define ERR CFlexError
\ No newline at end of file
diff --git a/SandboxiePlus/MiscHelpers/Common/FlowLayout.cpp b/SandboxiePlus/MiscHelpers/Common/FlowLayout.cpp
new file mode 100644
index 00000000..62115132
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/FlowLayout.cpp
@@ -0,0 +1,223 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+#include "FlowLayout.h"
+
+//! [1]
+QFlowLayout::QFlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing)
+ : QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing)
+{
+ setContentsMargins(margin, margin, margin, margin);
+}
+
+QFlowLayout::QFlowLayout(int margin, int hSpacing, int vSpacing)
+ : m_hSpace(hSpacing), m_vSpace(vSpacing)
+{
+ setContentsMargins(margin, margin, margin, margin);
+}
+//! [1]
+
+//! [2]
+QFlowLayout::~QFlowLayout()
+{
+ QLayoutItem *item;
+ while ((item = takeAt(0)))
+ delete item;
+}
+//! [2]
+
+//! [3]
+void QFlowLayout::addItem(QLayoutItem *item)
+{
+ itemList.append(item);
+}
+//! [3]
+
+//! [4]
+int QFlowLayout::horizontalSpacing() const
+{
+ if (m_hSpace >= 0) {
+ return m_hSpace;
+ } else {
+ return smartSpacing(QStyle::PM_LayoutHorizontalSpacing);
+ }
+}
+
+int QFlowLayout::verticalSpacing() const
+{
+ if (m_vSpace >= 0) {
+ return m_vSpace;
+ } else {
+ return smartSpacing(QStyle::PM_LayoutVerticalSpacing);
+ }
+}
+//! [4]
+
+//! [5]
+int QFlowLayout::count() const
+{
+ return itemList.size();
+}
+
+QLayoutItem *QFlowLayout::itemAt(int index) const
+{
+ return itemList.value(index);
+}
+
+QLayoutItem *QFlowLayout::takeAt(int index)
+{
+ if (index >= 0 && index < itemList.size())
+ return itemList.takeAt(index);
+ else
+ return 0;
+}
+//! [5]
+
+//! [6]
+Qt::Orientations QFlowLayout::expandingDirections() const
+{
+ return 0;
+}
+//! [6]
+
+//! [7]
+bool QFlowLayout::hasHeightForWidth() const
+{
+ return true;
+}
+
+int QFlowLayout::heightForWidth(int width) const
+{
+ int height = doLayout(QRect(0, 0, width, 0), true);
+ return height;
+}
+//! [7]
+
+//! [8]
+void QFlowLayout::setGeometry(const QRect &rect)
+{
+ QLayout::setGeometry(rect);
+ doLayout(rect, false);
+}
+
+QSize QFlowLayout::sizeHint() const
+{
+ return minimumSize();
+}
+
+QSize QFlowLayout::minimumSize() const
+{
+ QSize size;
+ QLayoutItem *item;
+ foreach (item, itemList)
+ size = size.expandedTo(item->minimumSize());
+
+ size += QSize(2*margin(), 2*margin());
+ return size;
+}
+//! [8]
+
+//! [9]
+int QFlowLayout::doLayout(const QRect &rect, bool testOnly) const
+{
+ int left, top, right, bottom;
+ getContentsMargins(&left, &top, &right, &bottom);
+ QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
+ int x = effectiveRect.x();
+ int y = effectiveRect.y();
+ int lineHeight = 0;
+//! [9]
+
+//! [10]
+ QLayoutItem *item;
+ foreach (item, itemList) {
+ QWidget *wid = item->widget();
+ int spaceX = horizontalSpacing();
+ if (spaceX == -1)
+ spaceX = wid->style()->layoutSpacing(
+ QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal);
+ int spaceY = verticalSpacing();
+ if (spaceY == -1)
+ spaceY = wid->style()->layoutSpacing(
+ QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);
+//! [10]
+//! [11]
+ int nextX = x + item->sizeHint().width() + spaceX;
+ if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) {
+ x = effectiveRect.x();
+ y = y + lineHeight + spaceY;
+ nextX = x + item->sizeHint().width() + spaceX;
+ lineHeight = 0;
+ }
+
+ if (!testOnly)
+ item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));
+
+ x = nextX;
+ lineHeight = qMax(lineHeight, item->sizeHint().height());
+ }
+ return y + lineHeight - rect.y() + bottom;
+}
+//! [11]
+//! [12]
+int QFlowLayout::smartSpacing(QStyle::PixelMetric pm) const
+{
+ QObject *parent = this->parent();
+ if (!parent) {
+ return -1;
+ } else if (parent->isWidgetType()) {
+ QWidget *pw = static_cast(parent);
+ return pw->style()->pixelMetric(pm, 0, pw);
+ } else {
+ return static_cast(parent)->spacing();
+ }
+}
+//! [12]
diff --git a/SandboxiePlus/MiscHelpers/Common/FlowLayout.h b/SandboxiePlus/MiscHelpers/Common/FlowLayout.h
new file mode 100644
index 00000000..2bdc3433
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/FlowLayout.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFlowLayout_H
+#define QFlowLayout_H
+
+#include
+#include
+#include
+//! [0]
+#include "../mischelpers_global.h"
+
+class MISCHELPERS_EXPORT QFlowLayout : public QLayout
+{
+public:
+ explicit QFlowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1);
+ explicit QFlowLayout(int margin = -1, int hSpacing = -1, int vSpacing = -1);
+ ~QFlowLayout();
+
+ void addItem(QLayoutItem *item) override;
+ int horizontalSpacing() const;
+ int verticalSpacing() const;
+ Qt::Orientations expandingDirections() const override;
+ bool hasHeightForWidth() const override;
+ int heightForWidth(int) const override;
+ int count() const override;
+ QLayoutItem *itemAt(int index) const override;
+ QSize minimumSize() const override;
+ void setGeometry(const QRect &rect) override;
+ QSize sizeHint() const override;
+ QLayoutItem *takeAt(int index) override;
+
+private:
+ int doLayout(const QRect &rect, bool testOnly) const;
+ int smartSpacing(QStyle::PixelMetric pm) const;
+
+ QList itemList;
+ int m_hSpace;
+ int m_vSpace;
+};
+//! [0]
+
+#endif // QFlowLayout_H
diff --git a/SandboxiePlus/MiscHelpers/Common/HistoryGraph.h b/SandboxiePlus/MiscHelpers/Common/HistoryGraph.h
new file mode 100644
index 00000000..a1b70012
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/HistoryGraph.h
@@ -0,0 +1,161 @@
+#pragma once
+
+#include "../mischelpers_global.h"
+
+class MISCHELPERS_EXPORT CHistoryGraph: public QObject
+{
+public:
+ CHistoryGraph(bool bSimpleMode = false, QColor BkG = Qt::white, QObject* parent = NULL) : QObject(parent) {
+ m_SimpleMode = bSimpleMode;
+ m_BkG = BkG;
+ }
+ ~CHistoryGraph() {}
+
+ void AddValue(int Id, QColor Color)
+ {
+ m_Values.insert(Id, { 0.0, Color });
+ }
+
+ void SetValue(int Id, float Value)
+ {
+ m_Values[Id].Value = Value;
+ }
+
+ void Update(int CellHeight, int CellWidth)
+ {
+ Update(m_Graph, m_BkG, m_Values, CellHeight, CellWidth, m_SimpleMode);
+ }
+
+ QImage GetImage() const { return m_Graph; }
+
+ struct SValue
+ {
+ float Value;
+ QColor Color;
+ };
+
+ static void Update(QImage& m_Graph, const QColor& m_BkG, const QMap& m_Values, int CellHeight, int CellWidth, bool m_SimpleMode = true)
+ {
+ // init / resize
+ if(m_Graph.height() != CellWidth /*|| m_Graph.width() != curHeight*/)
+ {
+ QImage Graph = QImage(CellHeight, CellWidth, QImage::Format_RGB32);
+ QPainter qp(&Graph);
+ qp.fillRect(-1, -1, CellHeight+1, CellWidth+1, m_BkG);
+ if (!m_Graph.isNull())
+ qp.drawImage(0, Graph.height() - m_Graph.height(), m_Graph);
+ m_Graph = Graph;
+ }
+
+ // shift
+ uchar *dest = m_Graph.bits();
+ int pos = m_Graph.sizeInBytes() - m_Graph.bytesPerLine();
+ memmove(dest, dest + m_Graph.bytesPerLine(), pos);
+ QPainter qp(&m_Graph);
+
+ // draw new data points
+ int top = 0;
+
+#if 0
+ foreach(const SValue& Value, m_Values)
+ {
+ int x = (float)(m_Graph.width()) * Value.Value;
+ if (x > top)
+ top = x;
+
+ qp.setPen(Value.Color);
+ qp.drawLine(0, m_Graph.height() - 1, x, m_Graph.height());
+ }
+
+ qp.setPen(Qt::white);
+ qp.drawLine(top, m_Graph.height() - 1, m_Graph.width()-1, m_Graph.height());
+#else
+ dest += pos;
+ memset(dest, 0, m_Graph.bytesPerLine()); // fill line black
+ ASSERT(m_Graph.depth() == 32);
+ int max = m_Graph.width();
+ //ASSERT(max * 4 == m_Graph.bytesPerLine());
+
+ foreach(const SValue& Value, m_Values)
+ {
+ int x = (float)(max) * Value.Value;
+ if (x > max)
+ x = max;
+ if (x > top)
+ top = x;
+ x *= 4;
+
+ int r, g, b;
+ Value.Color.getRgb(&r, &g, &b);
+
+
+ for (int i = 0; i < x; i += 4)
+ {
+ if (m_SimpleMode || *(quint32*)(dest + i) == 0)
+ {
+ dest[i ] = b;
+ dest[i + 1] = g;
+ dest[i + 2] = r;
+ }
+ else
+ {
+#define DIV 4/5
+ int cb = (int)dest[i ]*DIV + b*DIV;
+ int cg = (int)dest[i + 1]*DIV + g*DIV;
+ int cr = (int)dest[i + 2]*DIV + r*DIV;
+
+ dest[i ] = qMin(cb, 255);
+ dest[i + 1] = qMin(cg, 255);
+ dest[i + 2] = qMin(cr, 255);
+ }
+ }
+ }
+
+ // fill whats left of the line
+ /*top *= 4;
+ if (top < m_Graph.bytesPerLine()) // fill rest white
+ memset(dest + top, 0xFF, m_Graph.bytesPerLine() - top);*/
+ {
+ int r, g, b;
+ m_BkG.getRgb(&r, &g, &b);
+
+ top *= 4;
+ max *= 4;
+ for (int i = top; i < max; i += 4)
+ {
+ dest[i] = b;
+ dest[i + 1] = g;
+ dest[i + 2] = r;
+ }
+ }
+#endif
+ }
+
+protected:
+ QImage m_Graph;
+ QColor m_BkG;
+ QMap m_Values;
+ bool m_SimpleMode;
+};
+
+class CHistoryWidget: public QWidget
+{
+public:
+ CHistoryWidget(CHistoryGraph* pHistoryGraph, QWidget* parent = NULL) : QWidget(parent) { m_pHistoryGraph = pHistoryGraph; }
+ ~CHistoryWidget() {}
+
+protected:
+ void paintEvent(QPaintEvent* e)
+ {
+ if (m_pHistoryGraph)
+ {
+ QImage HistoryGraph = m_pHistoryGraph->GetImage();
+ QPainter qp(this);
+ qp.translate(width() - HistoryGraph.height() - 1, height());
+ qp.rotate(270);
+ qp.drawImage(0, 0, HistoryGraph);
+ }
+ }
+
+ QPointer m_pHistoryGraph;
+};
\ No newline at end of file
diff --git a/SandboxiePlus/MiscHelpers/Common/IconExtreactor.cpp b/SandboxiePlus/MiscHelpers/Common/IconExtreactor.cpp
new file mode 100644
index 00000000..fa12d489
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/IconExtreactor.cpp
@@ -0,0 +1,307 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWinExtras module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+#include "IconExtreactor.h"
+
+#include
+
+//#include
+
+#include
+#include
+#include
+#include
+#include
+
+
+/* This example demonstrates the Windows-specific image conversion
+ * functions. */
+
+/*struct PixmapEntry {
+ QString name;
+ QPixmap pixmap;
+};
+
+typedef QVector PixmapEntryList;*/
+
+/*std::wostream &operator<<(std::wostream &str, const QString &s)
+{
+#ifdef Q_OS_WIN
+ str << reinterpret_cast(s.utf16());
+#else
+ str << s.toStdWString();
+#endif
+ return str;
+}*/
+
+QString formatSize(const QSize &size)
+{
+ return QString::number(size.width()) + u'x' + QString::number(size.height());
+}
+
+// Extract icons contained in executable or DLL using the Win32 API ExtractIconEx()
+PixmapEntryList extractIcons(const QString &sourceFile, bool large)
+{
+ const QString nativeName = QDir::toNativeSeparators(sourceFile);
+ const auto *sourceFileC = reinterpret_cast(nativeName.utf16());
+ const UINT iconCount = ExtractIconEx(sourceFileC, -1, nullptr, nullptr, 0);
+ if (!iconCount) {
+ //std::wcerr << sourceFile << " does not appear to contain icons.\n";
+ return PixmapEntryList();
+ }
+
+ QScopedArrayPointer icons(new HICON[iconCount]);
+ const UINT extractedIconCount = large ?
+ ExtractIconEx(sourceFileC, 0, icons.data(), nullptr, iconCount) :
+ ExtractIconEx(sourceFileC, 0, nullptr, icons.data(), iconCount);
+ if (!extractedIconCount) {
+ qErrnoWarning("Failed to extract icons from %s", qPrintable(sourceFile));
+ return PixmapEntryList();
+ }
+
+ PixmapEntryList result;
+ result.reserve(int(extractedIconCount));
+
+ //std::wcout << sourceFile << " contains " << extractedIconCount << " icon(s).\n";
+
+ for (UINT i = 0; i < extractedIconCount; ++i) {
+ PixmapEntry entry;
+ entry.pixmap = QtWin::fromHICON(icons[i]);
+ if (entry.pixmap.isNull()) {
+ //std::wcerr << "Error converting icons.\n";
+ return PixmapEntryList();
+ }
+ entry.name = QString::fromLatin1("%1_%2x%3").arg(i, 3, 10, QLatin1Char('0'))
+ .arg(entry.pixmap.width()).arg(entry.pixmap.height());
+ result.append(entry);
+ }
+ return result;
+}
+
+// Helper for extracting large/jumbo icons available from Windows Vista onwards
+// via SHGetImageList().
+QPixmap pixmapFromShellImageList(int iImageList, const SHFILEINFO &info)
+{
+ QPixmap result;
+ // For MinGW:
+ const IID iID_IImageList = { 0x46eb5926, 0x582e, 0x4017, {0x9f, 0xdf, 0xe8, 0x99, 0x8d, 0xaa, 0x9, 0x50} };
+
+ IImageList *imageList = nullptr;
+ if (FAILED(SHGetImageList(iImageList, iID_IImageList, reinterpret_cast(&imageList))))
+ return result;
+
+ HICON hIcon = nullptr;
+ if (SUCCEEDED(imageList->GetIcon(info.iIcon, ILD_TRANSPARENT, &hIcon))) {
+ result = QtWin::fromHICON(hIcon);
+ DestroyIcon(hIcon);
+ }
+ return result;
+}
+
+// Extract icons that would be displayed by the Explorer (shell)
+PixmapEntryList extractShellIcons(const QString &sourceFile, bool addOverlays)
+{
+ enum { // Shell image list ids
+ sHIL_EXTRALARGE = 0x2, // 48x48 or user-defined
+ sHIL_JUMBO = 0x4 // 256x256 (Vista or later)
+ };
+
+ struct FlagEntry {
+ const char *name;
+ unsigned flags;
+ };
+
+ const FlagEntry modeEntries[] =
+ {
+ {"", 0},
+ {"open", SHGFI_OPENICON},
+ {"sel", SHGFI_SELECTED},
+ };
+ const FlagEntry standardSizeEntries[] =
+ {
+ {"s", SHGFI_SMALLICON},
+ {"l", SHGFI_LARGEICON},
+ {"sh", SHGFI_SHELLICONSIZE},
+ };
+
+ const QString nativeName = QDir::toNativeSeparators(sourceFile);
+ const auto *sourceFileC = reinterpret_cast(nativeName.utf16());
+
+ SHFILEINFO info;
+ unsigned int baseFlags = SHGFI_ICON | SHGFI_SYSICONINDEX | SHGFI_ICONLOCATION;
+ if (addOverlays)
+ baseFlags |= SHGFI_ADDOVERLAYS | SHGFI_OVERLAYINDEX;
+ if (!QFileInfo(sourceFile).isDir())
+ baseFlags |= SHGFI_USEFILEATTRIBUTES;
+
+ PixmapEntryList result;
+ for (auto modeEntry : modeEntries) {
+ const unsigned modeFlags = baseFlags | modeEntry.flags;
+ QString modePrefix = QLatin1String("_shell_");
+ if (modeEntry.name[0])
+ modePrefix += QLatin1String(modeEntry.name) + u'_';
+ for (auto standardSizeEntry : standardSizeEntries) {
+ const unsigned flags = modeFlags | standardSizeEntry.flags;
+ const QString prefix = modePrefix + QLatin1String(standardSizeEntry.name)
+ + u'_';
+ ZeroMemory(&info, sizeof(SHFILEINFO));
+ const HRESULT hr = SHGetFileInfo(sourceFileC, 0, &info, sizeof(SHFILEINFO), flags);
+ if (FAILED(hr)) {
+ _com_error error(hr);
+ //std::wcerr << "SHGetFileInfo() failed for \"" << nativeName << "\", "
+ // << std::hex << std::showbase << flags << std::dec << std::noshowbase
+ // << " (" << prefix << "): " << error.ErrorMessage() << '\n';
+ continue;
+ }
+
+ if (info.hIcon) {
+ PixmapEntry entry;
+ entry.pixmap = QtWin::fromHICON(info.hIcon);
+ DestroyIcon(info.hIcon);
+ if (entry.pixmap.isNull()) {
+ //std::wcerr << "Error converting icons.\n";
+ return PixmapEntryList();
+ }
+ entry.name = prefix + formatSize(entry.pixmap.size());
+
+ const int iconIndex = info.iIcon & 0xFFFFFF;
+ const int overlayIconIndex = info.iIcon >> 24;
+
+ //std::wcout << "Obtained icon #" << iconIndex;
+ //if (addOverlays)
+ // std::wcout << " (overlay #" << overlayIconIndex << ')';
+ //if (info.szDisplayName[0])
+ // std::wcout << " from " << QString::fromWCharArray(info.szDisplayName);
+ //std::wcout << " (" << entry.pixmap.width() << 'x'
+ // << entry.pixmap.height() << ") for " << std::hex << std::showbase << flags
+ // << std::dec << std::noshowbase << '\n';
+
+ result.append(entry);
+ }
+ } // for standardSizeEntryCount
+ // Windows Vista onwards: extract large/jumbo icons
+ if (info.hIcon) {
+ const QPixmap extraLarge = pixmapFromShellImageList(sHIL_EXTRALARGE, info);
+ if (!extraLarge.isNull()) {
+ PixmapEntry entry;
+ entry.pixmap = extraLarge;
+ entry.name = modePrefix + QLatin1String("xl_") + formatSize(extraLarge.size());
+ result.append(entry);
+ }
+ const QPixmap jumbo = pixmapFromShellImageList(sHIL_JUMBO, info);
+ if (!jumbo.isNull()) {
+ PixmapEntry entry;
+ entry.pixmap = jumbo;
+ entry.name = modePrefix + QLatin1String("jumbo_") + formatSize(extraLarge.size());
+ result.append(entry);
+ }
+ }
+ } // for modes
+ return result;
+}
+
+/*
+const char description[] =
+"\nExtracts Windows icons from executables, DLL or icon files and writes them\n"
+"out as numbered .png-files.\n"
+"When passing the --shell option, the icons displayed by Explorer are extracted.\n";
+
+
+#include
+#include
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+ QCoreApplication::setApplicationName(QStringLiteral("Icon Extractor"));
+ QCoreApplication::setOrganizationName(QStringLiteral("QtProject"));
+ QCoreApplication::setApplicationVersion(QLatin1String(QT_VERSION_STR));
+ QCommandLineParser parser;
+ parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsCompactedShortOptions);
+ parser.setApplicationDescription(QLatin1String(description));
+ parser.addHelpOption();
+ parser.addVersionOption();
+ const QCommandLineOption largeIconOption(QStringLiteral("large"), QStringLiteral("Extract large icons"));
+ parser.addOption(largeIconOption);
+ const QCommandLineOption shellIconOption(QStringLiteral("shell"), QStringLiteral("Extract shell icons using SHGetFileInfo()"));
+ parser.addOption(shellIconOption);
+ const QCommandLineOption shellOverlayOption(QStringLiteral("overlay"), QStringLiteral("Extract shell overlay icons"));
+ parser.addOption(shellOverlayOption);
+ parser.addPositionalArgument(QStringLiteral("file"), QStringLiteral("The file to open."));
+ parser.addPositionalArgument(QStringLiteral("image_file_folder"), QStringLiteral("The folder to store the images."));
+ parser.process(app);
+ const QStringList &positionalArguments = parser.positionalArguments();
+ if (positionalArguments.isEmpty())
+ parser.showHelp(0);
+
+ QString imageFileRoot = positionalArguments.size() > 1 ? positionalArguments.at(1) : QDir::currentPath();
+ const QFileInfo imageFileRootInfo(imageFileRoot);
+ if (!imageFileRootInfo.isDir()) {
+ std::wcerr << imageFileRoot << " is not a directory.\n";
+ return 1;
+ }
+ const QString &sourceFile = positionalArguments.constFirst();
+ imageFileRoot = imageFileRootInfo.absoluteFilePath() + u'/' + QFileInfo(sourceFile).baseName();
+
+ const PixmapEntryList pixmaps = parser.isSet(shellIconOption)
+ ? extractShellIcons(sourceFile, parser.isSet(shellOverlayOption))
+ : extractIcons(sourceFile, parser.isSet(largeIconOption));
+
+ for (const auto &entry : pixmaps) {
+ const QString fileName = imageFileRoot + entry.name + QLatin1String(".png");
+ if (!entry.pixmap.save(fileName)) {
+ std::wcerr << "Error writing image file " << fileName << ".\n";
+ return 1;
+ }
+ std::wcout << "Wrote " << QDir::toNativeSeparators(fileName) << ".\n";
+ }
+ return 0;
+}*/
\ No newline at end of file
diff --git a/SandboxiePlus/MiscHelpers/Common/IconExtreactor.h b/SandboxiePlus/MiscHelpers/Common/IconExtreactor.h
new file mode 100644
index 00000000..0edda02f
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/IconExtreactor.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "../mischelpers_global.h"
+
+struct PixmapEntry {
+ QString name;
+ QPixmap pixmap;
+};
+
+typedef QVector PixmapEntryList;
+
+MISCHELPERS_EXPORT PixmapEntryList extractIcons(const QString &sourceFile, bool large);
+MISCHELPERS_EXPORT PixmapEntryList extractShellIcons(const QString &sourceFile, bool addOverlays);
\ No newline at end of file
diff --git a/SandboxiePlus/MiscHelpers/Common/ItemChooser.cpp b/SandboxiePlus/MiscHelpers/Common/ItemChooser.cpp
new file mode 100644
index 00000000..4f0072e9
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/ItemChooser.cpp
@@ -0,0 +1,191 @@
+#include "stdafx.h"
+#include "ItemChooser.h"
+
+CItemChooser::CItemChooser(const QString& Prompt, QWidget *parent)
+ : QDialog(parent)
+{
+ this->setWindowTitle(tr("Item Chooser"));
+
+ m_pMainLayout = new QVBoxLayout();
+ this->setLayout(m_pMainLayout);
+
+ m_pLabel = new QLabel(Prompt.isNull() ? tr("Select items that will be used.") : Prompt, this);
+ m_pLabel->setWordWrap(true);
+ m_pMainLayout->addWidget(m_pLabel);
+
+ m_pCenterLayout = new QHBoxLayout();
+ m_pMainLayout->addLayout(m_pCenterLayout);
+
+ m_pListAll = new QListWidget(this);
+ m_pCenterLayout->addWidget(m_pListAll);
+ m_pListAll->setSelectionMode(QAbstractItemView::ExtendedSelection);
+ connect(m_pListAll, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(OnAdd()));
+
+ m_pMidleLayout = new QVBoxLayout();
+ m_pCenterLayout->addLayout(m_pMidleLayout);
+
+ m_pMidleLayout->addItem(new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding));
+
+ m_pBtnAdd = new QPushButton(tr("Add >"), this);
+ connect(m_pBtnAdd, SIGNAL(pressed()), this, SLOT(OnAdd()));
+ m_pMidleLayout->addWidget(m_pBtnAdd);
+
+ m_pBtnRemove = new QPushButton(tr("< Remove"), this);
+ connect(m_pBtnRemove, SIGNAL(pressed()), this, SLOT(OnRemove()));
+ m_pMidleLayout->addWidget(m_pBtnRemove);
+
+ m_pMidleLayout->addItem(new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding));
+
+ m_pListChoosen = new QListWidget(this);
+ m_pCenterLayout->addWidget(m_pListChoosen);
+ m_pListChoosen->setSelectionMode(QAbstractItemView::ExtendedSelection);
+ connect(m_pListChoosen, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(OnRemove()));
+
+ m_pRightLayout = new QVBoxLayout();
+ m_pCenterLayout->addLayout(m_pRightLayout);
+
+ m_pBtnUp = new QPushButton(tr("Move Up"), this);
+ connect(m_pBtnUp, SIGNAL(pressed()), this, SLOT(OnUp()));
+ m_pRightLayout->addWidget(m_pBtnUp);
+
+ m_pBtnDown = new QPushButton(tr("Move Down"), this);
+ connect(m_pBtnDown, SIGNAL(pressed()), this, SLOT(OnDown()));
+ m_pRightLayout->addWidget(m_pBtnDown);
+
+ m_pRightLayout->addItem(new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding));
+
+ m_InsertIndex = m_pMainLayout->count();
+
+ m_pButtonBox = new QDialogButtonBox(this);
+ m_pMainLayout->addWidget(m_pButtonBox);
+ m_pButtonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
+ connect(m_pButtonBox, SIGNAL(accepted()), this, SLOT(accept()));
+ connect(m_pButtonBox, SIGNAL(rejected()), this, SLOT(reject()));
+}
+
+void CItemChooser::closeEvent(QCloseEvent *e)
+{
+ this->deleteLater();
+}
+
+void CItemChooser::SetPrompt(const QString& Text)
+{
+ m_pLabel->setText(Text);
+}
+
+void CItemChooser::AddWidgets(QList Widgets)
+{
+ foreach(QWidget* pWidget, Widgets)
+ AddWidget(pWidget);
+}
+
+void CItemChooser::AddWidget(QWidget* pWidget)
+{
+ m_pMainLayout->insertWidget(m_InsertIndex++, pWidget);
+}
+
+void CItemChooser::AddItem(const QString& Label, const QVariant& Data)
+{
+ QListWidgetItem* pItem = new QListWidgetItem(Label);
+ pItem->setData(Qt::UserRole, Data);
+ m_pListAll->addItem(pItem);
+}
+
+void CItemChooser::OnAdd()
+{
+ foreach(QListWidgetItem* pItem, m_pListAll->selectedItems())
+ AddItem(pItem);
+}
+
+void CItemChooser::OnRemove()
+{
+ foreach(QListWidgetItem* pItem, m_pListChoosen->selectedItems())
+ RemoveItem(pItem);
+}
+
+void CItemChooser::AddItem(QListWidgetItem* pItem)
+{
+ pItem->setFlags(pItem->flags() & ~Qt::ItemIsSelectable & ~Qt::ItemIsEnabled);
+ if (m_pListChoosen->findItems(pItem->text(), Qt::MatchExactly).count() > 0)
+ return; // already added
+
+ QListWidgetItem* pChoosenItem = new QListWidgetItem(pItem->text());
+ pChoosenItem->setData(Qt::UserRole, pItem->data(Qt::UserRole));
+ m_pListChoosen->addItem(pChoosenItem);
+}
+
+void CItemChooser::RemoveItem(QListWidgetItem* pChoosenItem)
+{
+ foreach(QListWidgetItem* pItem, m_pListAll->findItems(pChoosenItem->text(), Qt::MatchExactly))
+ pItem->setFlags(pItem->flags() | Qt::ItemIsSelectable | Qt::ItemIsEnabled);
+ delete pChoosenItem;
+}
+
+void CItemChooser::OnUp()
+{
+ for (int i = 0; i < m_pListChoosen->count(); i++)
+ {
+ QListWidgetItem* pItem = m_pListChoosen->item(i);
+ if (!pItem->isSelected())
+ continue;
+ if (!MoveItem(pItem, -1))
+ break;
+ }
+}
+
+void CItemChooser::OnDown()
+{
+ for (int i = m_pListChoosen->count()-1; i >= 0 ; i--)
+ {
+ QListWidgetItem* pItem = m_pListChoosen->item(i);
+ if (!pItem->isSelected())
+ continue;
+ if (!MoveItem(pItem, 1))
+ break;
+ }
+}
+
+bool CItemChooser::MoveItem(QListWidgetItem* pItem, int Pos)
+{
+ int currIndex = m_pListChoosen->row(pItem);
+
+ QListWidgetItem *pPrev = m_pListChoosen->item(m_pListChoosen->row(pItem) + Pos);
+ if (!pPrev)
+ return false;
+ int prevIndex = m_pListChoosen->row(pPrev);
+
+ QListWidgetItem *pTemp = m_pListChoosen->takeItem(prevIndex);
+ m_pListChoosen->insertItem(prevIndex, pItem);
+ m_pListChoosen->insertItem(currIndex, pTemp);
+ return true;
+}
+
+QVariantList CItemChooser::GetChoosenItems()
+{
+ QVariantList ChoosenItems;
+ for(int i=0; i < m_pListChoosen->count(); i++)
+ ChoosenItems.append(m_pListChoosen->item(i)->data(Qt::UserRole));
+ return ChoosenItems;
+}
+
+void CItemChooser::ChooseItems(const QVariantList& ChoosenItems)
+{
+ foreach(const QVariant& Data, ChoosenItems)
+ ChooseItem(Data);
+}
+
+void CItemChooser::ChooseItem(const QVariant& Data)
+{
+ QListWidgetItem* pItem = NULL;
+ for (int i = 0; i < m_pListAll->count(); i++)
+ {
+ if (m_pListAll->item(i)->data(Qt::UserRole) == Data)
+ {
+ pItem = m_pListAll->item(i);
+ break;
+ }
+ }
+
+ if (pItem)
+ AddItem(pItem);
+}
\ No newline at end of file
diff --git a/SandboxiePlus/MiscHelpers/Common/ItemChooser.h b/SandboxiePlus/MiscHelpers/Common/ItemChooser.h
new file mode 100644
index 00000000..5379dbaf
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/ItemChooser.h
@@ -0,0 +1,61 @@
+#pragma once
+
+#include
+#include
+
+#include "../mischelpers_global.h"
+
+class MISCHELPERS_EXPORT CItemChooser : public QDialog
+{
+ Q_OBJECT
+
+public:
+ CItemChooser(const QString& Prompt = QString(), QWidget *parent = Q_NULLPTR);
+
+ void SetPrompt(const QString& Text);
+ void AddWidgets(QList Widgets);
+ void AddWidget(QWidget* pWidget);
+
+ void AddItem(const QString& Label, const QVariant& Data);
+ void ChooseItems(const QVariantList& ChoosenItems);
+ void ChooseItem(const QVariant& Data);
+
+ QVariantList GetChoosenItems();
+
+private slots:
+ void OnAdd();
+ void OnRemove();
+ void OnUp();
+ void OnDown();
+
+
+protected:
+ void closeEvent(QCloseEvent *e);
+
+ void AddItem(QListWidgetItem* pItem);
+ void RemoveItem(QListWidgetItem* pChoosenItem);
+ bool MoveItem(QListWidgetItem* pItem, int Pos);
+
+private:
+
+ QVBoxLayout* m_pMainLayout;
+ QLabel* m_pLabel;
+
+ QHBoxLayout* m_pCenterLayout;
+
+ QListWidget* m_pListAll;
+
+ QVBoxLayout* m_pMidleLayout;
+ QPushButton* m_pBtnAdd;
+ QPushButton* m_pBtnRemove;
+
+ QListWidget* m_pListChoosen;
+
+ QVBoxLayout* m_pRightLayout;
+ QPushButton* m_pBtnUp;
+ QPushButton* m_pBtnDown;
+
+ QDialogButtonBox* m_pButtonBox;
+
+ int m_InsertIndex;
+};
diff --git a/SandboxiePlus/MiscHelpers/Common/KeyValueInputDialog.cpp b/SandboxiePlus/MiscHelpers/Common/KeyValueInputDialog.cpp
new file mode 100644
index 00000000..e34f9c68
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/KeyValueInputDialog.cpp
@@ -0,0 +1,224 @@
+#include "stdafx.h"
+#include "KeyValueInputDialog.h"
+
+#include
+#include
+#include
+#include
+#include
+
+
+class CKeyValueInputDialogPrivate
+{
+public:
+ CKeyValueInputDialogPrivate(QDialog *q)
+ : clickedButton(0)
+ {
+ QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
+
+ pixmapLabel = new QLabel(q);
+ sizePolicy.setHorizontalStretch(0);
+ sizePolicy.setVerticalStretch(0);
+ sizePolicy.setHeightForWidth(pixmapLabel->sizePolicy().hasHeightForWidth());
+ pixmapLabel->setSizePolicy(sizePolicy);
+ pixmapLabel->setVisible(false);
+
+ QSpacerItem *pixmapSpacer =
+ new QSpacerItem(0, 5, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding);
+
+ messageLabel = new QLabel(q);
+ messageLabel->setMinimumSize(QSize(300, 0));
+ messageLabel->setWordWrap(true);
+ messageLabel->setOpenExternalLinks(true);
+ messageLabel->setTextInteractionFlags(Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse);
+
+ QSpacerItem *buttonSpacer =
+ new QSpacerItem(0, 1, QSizePolicy::Minimum, QSizePolicy::Minimum);
+
+ keyLabel = new QLabel(q);
+ keyLabel->setText("Key:");
+
+ keyEdit = new QLineEdit(q);
+
+ valueLabel = new QLabel(q);
+ valueLabel->setText("Value:");
+
+ valueEdit = new QTextEdit(q);
+
+
+ buttonBox = new QDialogButtonBox(q);
+ buttonBox->setOrientation(Qt::Horizontal);
+ buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
+
+ QVBoxLayout *verticalLayout = new QVBoxLayout();
+ verticalLayout->addWidget(pixmapLabel);
+ verticalLayout->addItem(pixmapSpacer);
+
+ QHBoxLayout *horizontalLayout_2 = new QHBoxLayout();
+ horizontalLayout_2->addLayout(verticalLayout);
+ horizontalLayout_2->addWidget(messageLabel);
+
+ QFormLayout *horizontalLayout = new QFormLayout();
+ horizontalLayout->addRow(keyLabel, keyEdit);
+ horizontalLayout->addRow(valueLabel, valueEdit);
+
+ QVBoxLayout *verticalLayout_2 = new QVBoxLayout(q);
+ verticalLayout_2->addLayout(horizontalLayout_2);
+ verticalLayout_2->addLayout(horizontalLayout);
+ verticalLayout_2->addItem(buttonSpacer);
+ verticalLayout_2->addWidget(buttonBox);
+ }
+
+ QLabel *pixmapLabel;
+ QLabel *messageLabel;
+ QLabel *keyLabel;
+ QLineEdit* keyEdit;
+ QLabel *valueLabel;
+ QTextEdit* valueEdit;
+ QDialogButtonBox *buttonBox;
+ QAbstractButton *clickedButton;
+};
+
+CKeyValueInputDialog::CKeyValueInputDialog(QWidget *parent) :
+ QDialog(parent),
+ d(new CKeyValueInputDialogPrivate(this))
+{
+ setModal(true);
+ setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
+ connect(d->buttonBox, SIGNAL(accepted()), SLOT(accept()));
+ connect(d->buttonBox, SIGNAL(rejected()), SLOT(reject()));
+ connect(d->buttonBox, SIGNAL(clicked(QAbstractButton*)),
+ SLOT(slotClicked(QAbstractButton*)));
+}
+
+CKeyValueInputDialog::~CKeyValueInputDialog()
+{
+ delete d;
+}
+
+void CKeyValueInputDialog::slotClicked(QAbstractButton *b)
+{
+ d->clickedButton = b;
+}
+
+QAbstractButton *CKeyValueInputDialog::clickedButton() const
+{
+ return d->clickedButton;
+}
+
+QDialogButtonBox::StandardButton CKeyValueInputDialog::clickedStandardButton() const
+{
+ if (d->clickedButton)
+ return d->buttonBox->standardButton(d->clickedButton);
+ return QDialogButtonBox::NoButton;
+}
+
+QString CKeyValueInputDialog::text() const
+{
+ return d->messageLabel->text();
+}
+
+void CKeyValueInputDialog::setText(const QString &t)
+{
+ d->messageLabel->setText(t);
+}
+
+QString CKeyValueInputDialog::keyLabel() const
+{
+ return d->keyLabel->text();
+}
+
+void CKeyValueInputDialog::setKeyLabel(const QString &t)
+{
+ return d->keyLabel->setText(t);
+}
+
+QString CKeyValueInputDialog::key() const
+{
+ return d->keyEdit->text();
+}
+
+void CKeyValueInputDialog::setKey(const QString &t)
+{
+ return d->keyEdit->setText(t);
+}
+
+bool CKeyValueInputDialog::keyReadOnly() const
+{
+ return d->keyEdit->isReadOnly();
+}
+
+void CKeyValueInputDialog::setKeyReadOnly(bool r)
+{
+ return d->keyEdit->setReadOnly(r);
+}
+
+QString CKeyValueInputDialog::valueLabel() const
+{
+ return d->valueLabel->text();
+}
+
+void CKeyValueInputDialog::setValueLabel(const QString &t)
+{
+ return d->valueLabel->setText(t);
+}
+
+QString CKeyValueInputDialog::value() const
+{
+ return d->valueEdit->toPlainText();
+}
+
+void CKeyValueInputDialog::setValue(const QString &t)
+{
+ return d->valueEdit->setText(t);
+}
+
+QPixmap CKeyValueInputDialog::iconPixmap() const
+{
+ if (const QPixmap *p = d->pixmapLabel->pixmap())
+ return QPixmap(*p);
+ return QPixmap();
+}
+
+void CKeyValueInputDialog::setIconPixmap(const QPixmap &p)
+{
+ d->pixmapLabel->setPixmap(p);
+ d->pixmapLabel->setVisible(!p.isNull());
+}
+
+QDialogButtonBox::StandardButtons CKeyValueInputDialog::standardButtons() const
+{
+ return d->buttonBox->standardButtons();
+}
+
+void CKeyValueInputDialog::setStandardButtons(QDialogButtonBox::StandardButtons s)
+{
+ d->buttonBox->setStandardButtons(s);
+}
+
+QPushButton *CKeyValueInputDialog::button(QDialogButtonBox::StandardButton b) const
+{
+ return d->buttonBox->button(b);
+}
+
+QPushButton *CKeyValueInputDialog::addButton(const QString &text, QDialogButtonBox::ButtonRole role)
+{
+ return d->buttonBox->addButton(text, role);
+}
+
+QDialogButtonBox::StandardButton CKeyValueInputDialog::defaultButton() const
+{
+ foreach (QAbstractButton *b, d->buttonBox->buttons())
+ if (QPushButton *pb = qobject_cast(b))
+ if (pb->isDefault())
+ return d->buttonBox->standardButton(pb);
+ return QDialogButtonBox::NoButton;
+}
+
+void CKeyValueInputDialog::setDefaultButton(QDialogButtonBox::StandardButton s)
+{
+ if (QPushButton *b = d->buttonBox->button(s)) {
+ b->setDefault(true);
+ b->setFocus();
+ }
+}
diff --git a/SandboxiePlus/MiscHelpers/Common/KeyValueInputDialog.h b/SandboxiePlus/MiscHelpers/Common/KeyValueInputDialog.h
new file mode 100644
index 00000000..b7d5b90e
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/KeyValueInputDialog.h
@@ -0,0 +1,67 @@
+#ifndef KEYVALUEINPUTDIALOG_H
+#define KEYVALUEINPUTDIALOG_H
+
+#include
+#include
+
+class CKeyValueInputDialogPrivate;
+
+#include "../mischelpers_global.h"
+
+class MISCHELPERS_EXPORT CKeyValueInputDialog : public QDialog
+{
+ Q_OBJECT
+ Q_PROPERTY(QString text READ text WRITE setText)
+ Q_PROPERTY(QPixmap iconPixmap READ iconPixmap WRITE setIconPixmap)
+ Q_PROPERTY(QDialogButtonBox::StandardButtons buttons READ standardButtons WRITE setStandardButtons)
+ Q_PROPERTY(QDialogButtonBox::StandardButton defaultButton READ defaultButton WRITE setDefaultButton)
+
+public:
+ explicit CKeyValueInputDialog(QWidget *parent = NULL);
+ virtual ~CKeyValueInputDialog();
+
+ QString text() const;
+ void setText(const QString &);
+
+ QString keyLabel() const;
+ void setKeyLabel(const QString &);
+
+ QString key() const;
+ void setKey(const QString &);
+
+ bool keyReadOnly() const;
+ void setKeyReadOnly(bool);
+
+ QString valueLabel() const;
+ void setValueLabel(const QString &);
+
+ QString value() const;
+ void setValue(const QString &);
+
+
+ QDialogButtonBox::StandardButtons standardButtons() const;
+ void setStandardButtons(QDialogButtonBox::StandardButtons s);
+ QPushButton *button(QDialogButtonBox::StandardButton b) const;
+ QPushButton *addButton(const QString &text, QDialogButtonBox::ButtonRole role);
+
+ QDialogButtonBox::StandardButton defaultButton() const;
+ void setDefaultButton(QDialogButtonBox::StandardButton s);
+
+ // See static QMessageBox::standardPixmap()
+ QPixmap iconPixmap() const;
+ void setIconPixmap (const QPixmap &p);
+
+ // Query the result
+ QAbstractButton *clickedButton() const;
+ QDialogButtonBox::StandardButton clickedStandardButton() const;
+
+private slots:
+ void slotClicked(QAbstractButton *b);
+
+private:
+ CKeyValueInputDialogPrivate *d;
+};
+
+
+
+#endif // HCOMBODIALOG_H
diff --git a/SandboxiePlus/MiscHelpers/Common/ListItemModel.cpp b/SandboxiePlus/MiscHelpers/Common/ListItemModel.cpp
new file mode 100644
index 00000000..fbec6a81
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/ListItemModel.cpp
@@ -0,0 +1,305 @@
+#include "stdafx.h"
+#include "ListItemModel.h"
+
+#define FIRST_COLUMN 0
+
+bool CListItemModel::m_DarkMode = false;
+
+CListItemModel::CListItemModel(QObject *parent)
+: QAbstractItemModelEx(parent)
+{
+ m_bUseIcons = false;
+}
+
+CListItemModel::~CListItemModel()
+{
+ foreach(SListNode* pNode, m_List)
+ delete pNode;
+}
+
+int CListItemModel::GetRow(SListNode* pNode) const
+{
+ int Index = m_RevList.value(pNode, -1);
+ ASSERT(Index != -1);
+ ASSERT(m_List[Index] == pNode);
+ return Index;
+}
+
+void CSimpleListModel::Sync(QList List)
+{
+ QList New;
+ QHash Old = m_Map;
+
+ foreach (const QVariantMap& Cur, List)
+ {
+ QVariant ID = Cur["ID"];
+
+ int Row = -1;
+ SListNode* pNode = static_cast(Old[ID]);
+ if(!pNode)
+ {
+ pNode = static_cast(MkNode(ID));
+ pNode->Values.resize(columnCount());
+ pNode->IsBold = Cur["IsBold"].toBool();
+ pNode->Icon = Cur["Icon"];
+ New.append(pNode);
+ }
+ else
+ {
+ Old[ID] = NULL;
+ Row = GetRow(pNode);
+ }
+
+ int Col = 0;
+ bool State = false;
+ bool Changed = false;
+
+ QVariantMap Values = Cur["Values"].toMap();
+ for(int section = FIRST_COLUMN; section < columnCount(); section++)
+ {
+ if (!m_Columns.contains(section))
+ continue; // ignore columns which are hidden
+
+ QVariant Value = Values[QString::number(section)];
+
+ SListNode::SValue& ColValue = pNode->Values[section];
+
+ if (ColValue.Raw != Value)
+ {
+ Changed = true;
+ ColValue.Raw = Value;
+
+ //ColValue.Formated =
+ }
+
+ if(State != Changed)
+ {
+ if(State && Row != -1)
+ emit dataChanged(createIndex(Row, Col), createIndex(Row, section-1));
+ State = Changed;
+ Col = section;
+ }
+ Changed = false;
+ }
+ if(State && Row != -1)
+ emit dataChanged(createIndex(Row, Col, pNode), createIndex(Row, columnCount()-1, pNode));
+ }
+
+ CListItemModel::Sync(New, Old);
+}
+
+void CListItemModel::Sync(QList& New, QHash& Old)
+{
+ int Removed = 0;
+
+ int Begin = -1;
+ int End = -1;
+ for(int i = m_List.count()-1; i >= -1; i--)
+ {
+ QVariant ID = i >= 0 ? m_List[i]->ID : QVariant();
+ if(!ID.isNull() && (Old.value(ID) != NULL)) // remove it
+ {
+ m_Map.remove(ID);
+ if(End == -1)
+ End = i;
+ }
+ else if(End != -1) // keep it and remove whatis to be removed at once
+ {
+ Begin = i + 1;
+
+ beginRemoveRows(QModelIndex(), Begin, End);
+ for (int j = End; j >= Begin; j--)
+ {
+ Removed++;
+
+ SListNode* pNode = m_List.takeAt(j);
+ m_RevList.remove(pNode);
+ delete pNode;
+ }
+ endRemoveRows();
+
+ End = -1;
+ Begin = -1;
+ }
+ }
+
+ // if something was removed we need to update the row cache
+ if (Removed > 0)
+ {
+ ASSERT(m_RevList.size() == m_List.size());
+ for (int i = 0; i < m_List.count(); i++)
+ m_RevList[m_List[i]] = i;
+ }
+
+ Begin = m_List.count();
+ for(QList::iterator I = New.begin(); I != New.end(); I++)
+ {
+ SListNode* pNode = *I;
+ m_Map.insert(pNode->ID, pNode);
+
+ int Index = m_List.size();
+ m_List.append(pNode);
+ m_RevList.insert(pNode, Index);
+ }
+ End = m_List.count();
+ if(Begin < End)
+ {
+ beginInsertRows(QModelIndex(), Begin, End-1);
+ endInsertRows();
+ }
+}
+
+QModelIndex CListItemModel::FindIndex(const QVariant& ID)
+{
+ if(SListNode* pNode = m_Map.value(ID))
+ {
+ int row = m_List.indexOf(pNode);
+ ASSERT(row != -1);
+ return createIndex(row, FIRST_COLUMN, pNode);
+ }
+ return QModelIndex();
+}
+
+void CListItemModel::Clear()
+{
+ //beginResetModel();
+ if (rowCount() == 0)
+ return;
+ beginRemoveRows(QModelIndex(), 0, rowCount()-1);
+ foreach(SListNode* pNode, m_List)
+ delete pNode;
+ m_List.clear();
+ m_RevList.clear();
+ m_Map.clear();
+ endRemoveRows();
+ //endResetModel();
+}
+
+QVariant CListItemModel::data(const QModelIndex &index, int role) const
+{
+ return Data(index, role, index.column());
+}
+
+QVariant CListItemModel::Data(const QModelIndex &index, int role, int section) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ //if(role == Qt::SizeHintRole )
+ // return QSize(64,16); // for fixing height
+
+ SListNode* pNode = static_cast(index.internalPointer());
+
+ switch(role)
+ {
+ case Qt::DisplayRole:
+ {
+ SListNode::SValue& Value = pNode->Values[section];
+ return Value.Formated.isValid() ? Value.Formated : Value.Raw;
+ }
+ case Qt::EditRole: // sort role
+ {
+ return pNode->Values[section].Raw;
+ }
+ case Qt::ToolTipRole:
+ {
+ QString ToolTip;
+ emit ToolTipCallback(pNode->ID, ToolTip);
+ if(!ToolTip.isNull())
+ return ToolTip;
+ break;
+ }
+ case Qt::DecorationRole:
+ {
+ if (m_bUseIcons && section == FIRST_COLUMN)
+ return pNode->Icon.isValid() ? pNode->Icon : GetDefaultIcon();
+ break;
+ }
+ case Qt::FontRole:
+ {
+ if (section == FIRST_COLUMN && pNode->IsBold)
+ {
+ QFont fnt;
+ fnt.setBold(true);
+ return fnt;
+ }
+ break;
+ }
+ case Qt::BackgroundRole:
+ {
+ if(!m_DarkMode)
+ return pNode->Color.isValid() ? pNode->Color : QVariant();
+ break;
+ }
+ case Qt::ForegroundRole:
+ {
+ if(m_DarkMode)
+ return pNode->Color.isValid() ? pNode->Color : QVariant();
+ else if (pNode->IsGray)
+ {
+ QColor Color = Qt::darkGray;
+ return QBrush(Color);
+ }
+ break;
+ }
+
+ case Qt::UserRole:
+ {
+ switch(section)
+ {
+ case FIRST_COLUMN: return pNode->ID;
+ }
+ break;
+ }
+ }
+ return QVariant();
+}
+
+Qt::ItemFlags CListItemModel::flags(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return 0;
+ return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
+}
+
+QModelIndex CListItemModel::index(int row, int column, const QModelIndex &parent) const
+{
+ if (!hasIndex(row, column, parent))
+ return QModelIndex();
+
+ if (parent.isValid())
+ return QModelIndex();
+ if(m_List.count() > row)
+ return createIndex(row, column, m_List[row]);
+ return QModelIndex();
+}
+
+QModelIndex CListItemModel::parent(const QModelIndex &index) const
+{
+ return QModelIndex();
+}
+
+int CListItemModel::rowCount(const QModelIndex &parent) const
+{
+ if (parent.column() > 0)
+ return 0;
+
+ if (parent.isValid())
+ return 0;
+ return m_List.count();
+}
+
+int CSimpleListModel::columnCount(const QModelIndex &parent) const
+{
+ return m_Headers.count();
+}
+
+QVariant CSimpleListModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
+ {
+ if (section < m_Headers.size())
+ return m_Headers.at(section);
+ }
+ return QVariant();
+}
\ No newline at end of file
diff --git a/SandboxiePlus/MiscHelpers/Common/ListItemModel.h b/SandboxiePlus/MiscHelpers/Common/ListItemModel.h
new file mode 100644
index 00000000..13cfc7d6
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/ListItemModel.h
@@ -0,0 +1,98 @@
+#pragma once
+#include "TreeViewEx.h"
+
+#include "../mischelpers_global.h"
+
+class MISCHELPERS_EXPORT CListItemModel : public QAbstractItemModelEx
+{
+ Q_OBJECT
+
+public:
+ CListItemModel(QObject *parent = 0);
+ virtual ~CListItemModel();
+
+ void SetUseIcons(bool bUseIcons) { m_bUseIcons = bUseIcons; }
+ static void SetDarkMode(bool bDark) { m_DarkMode = bDark;}
+
+ QModelIndex FindIndex(const QVariant& ID);
+
+ QVariant Data(const QModelIndex &index, int role, int section) const;
+
+ // derived functions
+ virtual QVariant data(const QModelIndex &index, int role) const;
+ virtual Qt::ItemFlags flags(const QModelIndex &index) const;
+ virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
+ virtual QModelIndex parent(const QModelIndex &index) const;
+ virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ virtual int columnCount(const QModelIndex &parent = QModelIndex()) const = 0;
+ virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const = 0;
+
+public slots:
+ void Clear();
+
+signals:
+ void ToolTipCallback(const QVariant& ID, QString& ToolTip) const;
+
+protected:
+ struct SListNode
+ {
+ SListNode(const QVariant& Id)
+ {
+ ID = Id;
+
+ IsBold = false;
+ IsGray = false;
+ }
+ virtual ~SListNode()
+ {
+ }
+
+ QVariant ID;
+
+ QVariant Icon;
+ bool IsBold;
+ bool IsGray;
+ QColor Color;
+ struct SValue
+ {
+ QVariant Raw;
+ QVariant Formated;
+ };
+ QVector Values;
+ };
+
+ virtual SListNode* MkNode(const QVariant& Id) = 0; // { return new SListNode(Id); }
+
+ void Sync(QList& New, QHash& Old);
+
+ virtual QVariant GetDefaultIcon() const { return QVariant(); }
+
+ int GetRow(SListNode* pNode) const;
+
+ QList m_List;
+ QHash m_RevList;
+ QHash m_Map;
+ bool m_bUseIcons;
+
+ static bool m_DarkMode;
+};
+
+class MISCHELPERS_EXPORT CSimpleListModel : public CListItemModel
+{
+ Q_OBJECT
+
+public:
+ CSimpleListModel(QObject *parent = 0) : CListItemModel(parent) {}
+
+ void Sync(QList List);
+
+ void setHeaderLabels(const QStringList& Columns) { m_Headers = Columns; }
+
+ virtual int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
+
+protected:
+ virtual SListNode* MkNode(const QVariant& Id) { return new SListNode(Id); }
+
+ QStringList m_Headers;
+};
\ No newline at end of file
diff --git a/SandboxiePlus/MiscHelpers/Common/MultiLineInputDialog.cpp b/SandboxiePlus/MiscHelpers/Common/MultiLineInputDialog.cpp
new file mode 100644
index 00000000..c37f9cbd
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/MultiLineInputDialog.cpp
@@ -0,0 +1,169 @@
+#include "stdafx.h"
+#include "MultiLineInputDialog.h"
+
+#include
+#include
+#include
+#include
+#include
+
+
+class CMultiLineInputDialogPrivate
+{
+public:
+ CMultiLineInputDialogPrivate(QDialog *q)
+ : clickedButton(0)
+ {
+ QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
+
+ pixmapLabel = new QLabel(q);
+ sizePolicy.setHorizontalStretch(0);
+ sizePolicy.setVerticalStretch(0);
+ sizePolicy.setHeightForWidth(pixmapLabel->sizePolicy().hasHeightForWidth());
+ pixmapLabel->setSizePolicy(sizePolicy);
+ pixmapLabel->setVisible(false);
+
+ QSpacerItem *pixmapSpacer =
+ new QSpacerItem(0, 5, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding);
+
+ messageLabel = new QLabel(q);
+ messageLabel->setMinimumSize(QSize(300, 0));
+ messageLabel->setWordWrap(true);
+ messageLabel->setOpenExternalLinks(true);
+ messageLabel->setTextInteractionFlags(Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse);
+
+ QSpacerItem *buttonSpacer =
+ new QSpacerItem(0, 1, QSizePolicy::Minimum, QSizePolicy::Minimum);
+
+ edit = new QPlainTextEdit(q);
+
+
+ buttonBox = new QDialogButtonBox(q);
+ buttonBox->setOrientation(Qt::Horizontal);
+ buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
+
+ QVBoxLayout *verticalLayout = new QVBoxLayout();
+ verticalLayout->addWidget(pixmapLabel);
+ verticalLayout->addItem(pixmapSpacer);
+
+ QHBoxLayout *horizontalLayout_2 = new QHBoxLayout();
+ horizontalLayout_2->addLayout(verticalLayout);
+ horizontalLayout_2->addWidget(messageLabel);
+
+ QVBoxLayout *verticalLayout_2 = new QVBoxLayout(q);
+ verticalLayout_2->addLayout(horizontalLayout_2);
+ verticalLayout_2->addWidget(edit);
+ verticalLayout_2->addItem(buttonSpacer);
+ verticalLayout_2->addWidget(buttonBox);
+ }
+
+ QLabel *pixmapLabel;
+ QLabel *messageLabel;
+ QPlainTextEdit* edit;
+ QDialogButtonBox *buttonBox;
+ QAbstractButton *clickedButton;
+};
+
+CMultiLineInputDialog::CMultiLineInputDialog(QWidget *parent) :
+ QDialog(parent),
+ d(new CMultiLineInputDialogPrivate(this))
+{
+ setModal(true);
+ setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
+ connect(d->buttonBox, SIGNAL(accepted()), SLOT(accept()));
+ connect(d->buttonBox, SIGNAL(rejected()), SLOT(reject()));
+ connect(d->buttonBox, SIGNAL(clicked(QAbstractButton*)),
+ SLOT(slotClicked(QAbstractButton*)));
+}
+
+CMultiLineInputDialog::~CMultiLineInputDialog()
+{
+ delete d;
+}
+
+void CMultiLineInputDialog::slotClicked(QAbstractButton *b)
+{
+ d->clickedButton = b;
+}
+
+QAbstractButton *CMultiLineInputDialog::clickedButton() const
+{
+ return d->clickedButton;
+}
+
+QDialogButtonBox::StandardButton CMultiLineInputDialog::clickedStandardButton() const
+{
+ if (d->clickedButton)
+ return d->buttonBox->standardButton(d->clickedButton);
+ return QDialogButtonBox::NoButton;
+}
+
+QString CMultiLineInputDialog::text() const
+{
+ return d->messageLabel->text();
+}
+
+void CMultiLineInputDialog::setText(const QString &t)
+{
+ d->messageLabel->setText(t);
+}
+
+QString CMultiLineInputDialog::value() const
+{
+ return d->edit->toPlainText();
+}
+
+void CMultiLineInputDialog::setValue(const QString &t)
+{
+ d->edit->setPlainText(t);
+}
+
+QPixmap CMultiLineInputDialog::iconPixmap() const
+{
+ if (const QPixmap *p = d->pixmapLabel->pixmap())
+ return QPixmap(*p);
+ return QPixmap();
+}
+
+void CMultiLineInputDialog::setIconPixmap(const QPixmap &p)
+{
+ d->pixmapLabel->setPixmap(p);
+ d->pixmapLabel->setVisible(!p.isNull());
+}
+
+QDialogButtonBox::StandardButtons CMultiLineInputDialog::standardButtons() const
+{
+ return d->buttonBox->standardButtons();
+}
+
+void CMultiLineInputDialog::setStandardButtons(QDialogButtonBox::StandardButtons s)
+{
+ d->buttonBox->setStandardButtons(s);
+}
+
+QPushButton *CMultiLineInputDialog::button(QDialogButtonBox::StandardButton b) const
+{
+ return d->buttonBox->button(b);
+}
+
+QPushButton *CMultiLineInputDialog::addButton(const QString &text, QDialogButtonBox::ButtonRole role)
+{
+ return d->buttonBox->addButton(text, role);
+}
+
+QDialogButtonBox::StandardButton CMultiLineInputDialog::defaultButton() const
+{
+ foreach (QAbstractButton *b, d->buttonBox->buttons())
+ if (QPushButton *pb = qobject_cast(b))
+ if (pb->isDefault())
+ return d->buttonBox->standardButton(pb);
+ return QDialogButtonBox::NoButton;
+}
+
+void CMultiLineInputDialog::setDefaultButton(QDialogButtonBox::StandardButton s)
+{
+ if (QPushButton *b = d->buttonBox->button(s)) {
+ b->setDefault(true);
+ b->setFocus();
+ }
+}
diff --git a/SandboxiePlus/MiscHelpers/Common/MultiLineInputDialog.h b/SandboxiePlus/MiscHelpers/Common/MultiLineInputDialog.h
new file mode 100644
index 00000000..6a343d16
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/MultiLineInputDialog.h
@@ -0,0 +1,55 @@
+#ifndef MULTILINEINPUTDIALOG_H
+#define MULTILINEINPUTDIALOG_H
+
+#include
+#include
+
+class CMultiLineInputDialogPrivate;
+
+#include "../mischelpers_global.h"
+
+class MISCHELPERS_EXPORT CMultiLineInputDialog : public QDialog
+{
+ Q_OBJECT
+ Q_PROPERTY(QString text READ text WRITE setText)
+ Q_PROPERTY(QPixmap iconPixmap READ iconPixmap WRITE setIconPixmap)
+ Q_PROPERTY(QDialogButtonBox::StandardButtons buttons READ standardButtons WRITE setStandardButtons)
+ Q_PROPERTY(QDialogButtonBox::StandardButton defaultButton READ defaultButton WRITE setDefaultButton)
+
+public:
+ explicit CMultiLineInputDialog(QWidget *parent = NULL);
+ virtual ~CMultiLineInputDialog();
+
+ QString text() const;
+ void setText(const QString &);
+
+ QString value() const;
+ void setValue(const QString &);
+
+
+ QDialogButtonBox::StandardButtons standardButtons() const;
+ void setStandardButtons(QDialogButtonBox::StandardButtons s);
+ QPushButton *button(QDialogButtonBox::StandardButton b) const;
+ QPushButton *addButton(const QString &text, QDialogButtonBox::ButtonRole role);
+
+ QDialogButtonBox::StandardButton defaultButton() const;
+ void setDefaultButton(QDialogButtonBox::StandardButton s);
+
+ // See static QMessageBox::standardPixmap()
+ QPixmap iconPixmap() const;
+ void setIconPixmap (const QPixmap &p);
+
+ // Query the result
+ QAbstractButton *clickedButton() const;
+ QDialogButtonBox::StandardButton clickedStandardButton() const;
+
+private slots:
+ void slotClicked(QAbstractButton *b);
+
+private:
+ CMultiLineInputDialogPrivate *d;
+};
+
+
+
+#endif // HMULTILINEINPUTDIALOG_H
diff --git a/SandboxiePlus/MiscHelpers/Common/PanelView.cpp b/SandboxiePlus/MiscHelpers/Common/PanelView.cpp
new file mode 100644
index 00000000..6ead11a2
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/PanelView.cpp
@@ -0,0 +1,220 @@
+#include "stdafx.h"
+#include "PanelView.h"
+
+bool CPanelView::m_SimpleFormat = false;
+int CPanelView::m_MaxCellWidth = 0;
+QString CPanelView::m_CellSeparator = "\t";
+
+CPanelView::CPanelView(QWidget *parent)
+ :QWidget(parent)
+{
+ //m_CopyAll = false;
+
+ m_pMenu = new QMenu();
+}
+
+CPanelView::~CPanelView()
+{
+}
+
+void CPanelView::AddPanelItemsToMenu(bool bAddSeparator)
+{
+ if(bAddSeparator)
+ m_pMenu->addSeparator();
+ m_pCopyCell = m_pMenu->addAction(tr("Copy Cell"), this, SLOT(OnCopyCell()));
+ m_pCopyRow = m_pMenu->addAction(tr("Copy Row"), this, SLOT(OnCopyRow()));
+ m_pCopyRow->setShortcut(QKeySequence::Copy);
+ m_pCopyRow->setShortcutContext(Qt::WidgetWithChildrenShortcut);
+ this->addAction(m_pCopyRow);
+ m_pCopyPanel = m_pMenu->addAction(tr("Copy Panel"), this, SLOT(OnCopyPanel()));
+}
+
+void CPanelView::OnMenu(const QPoint& Point)
+{
+ QModelIndex Index = GetView()->currentIndex();
+
+ m_pCopyCell->setEnabled(Index.isValid());
+ m_pCopyRow->setEnabled(Index.isValid());
+
+ m_pMenu->popup(QCursor::pos());
+}
+
+void CPanelView::OnCopyCell()
+{
+ QAbstractItemModel* pModel = GetModel();
+ QTreeView * pView = GetView();
+
+ QModelIndex Index = pView->currentIndex();
+ QModelIndex ModelIndex = MapToSource(Index);
+ int Column = ModelIndex.column();
+
+ QList Rows;
+ foreach(const QModelIndex& Index, pView->selectionModel()->selectedIndexes())
+ {
+ if (Index.column() != Column)
+ continue;
+ QModelIndex CurIndex = pModel->index(Index.row(), Column, Index.parent());
+ QString Cell = pModel->data(CurIndex, Qt::DisplayRole).toString();
+ Rows.append(QStringList() << Cell);
+ }
+ FormatAndCopy(Rows, false);
+}
+
+void CPanelView::OnCopyRow()
+{
+ QAbstractItemModel* pModel = GetModel();
+ QTreeView * pView = GetView();
+
+ int Column = 0; // find first not hidden column
+ for (int i = 0; i < pModel->columnCount(); i++)
+ {
+ if (!pView->isColumnHidden(i) || m_ForcedColumns.contains(i))
+ {
+ Column = i;
+ break;
+ }
+ }
+
+ QList Rows;
+ foreach(const QModelIndex& Index, pView->selectionModel()->selectedIndexes())
+ {
+ if (Index.column() != Column)
+ continue;
+
+ QModelIndex ModelIndex = MapToSource(Index);
+ Rows.append(CopyRow(ModelIndex));
+ }
+ FormatAndCopy(Rows);
+}
+
+QStringList CPanelView::CopyHeader()
+{
+ QAbstractItemModel* pModel = GetModel();
+ QTreeView * pView = GetView();
+
+ QStringList Headder;
+ for (int i = 0; i < pModel->columnCount(); i++)
+ {
+ if (/*!m_CopyAll &&*/ pView->isColumnHidden(i) && !m_ForcedColumns.contains(i))
+ continue;
+ QString Cell = pModel->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString();
+ if (!m_SimpleFormat)
+ Cell = "|" + Cell + "|";
+ Headder.append(Cell);
+ }
+ return Headder;
+}
+
+QStringList CPanelView::CopyRow(const QModelIndex& ModelIndex, int Level)
+{
+ QAbstractItemModel* pModel = GetModel();
+ QTreeView * pView = GetView();
+
+ QStringList Cells;
+ for (int i = 0; i < pModel->columnCount(); i++)
+ {
+ if (/*!m_CopyAll &&*/ pView->isColumnHidden(i) && !m_ForcedColumns.contains(i))
+ continue;
+ QModelIndex CellIndex = pModel->index(ModelIndex.row(), i, ModelIndex.parent());
+ QString Cell = pModel->data(CellIndex, Qt::DisplayRole).toString();
+ if (Level && i == 0)
+ Cell.prepend(QString(Level, '_') + " ");
+ Cells.append(Cell);
+ }
+ return Cells;
+}
+
+void CPanelView::RecursiveCopyPanel(const QModelIndex& ModelIndex, QList& Rows, int Level)
+{
+ QAbstractItemModel* pModel = GetModel();
+
+ Rows.append(CopyRow(ModelIndex, Level));
+
+ for (int i = 0; i < pModel->rowCount(ModelIndex); i++)
+ {
+ QModelIndex SubIndex = pModel->index(i, 0, ModelIndex);
+ RecursiveCopyPanel(SubIndex, Rows, Level + 1);
+ }
+}
+
+void CPanelView::OnCopyPanel()
+{
+ QAbstractItemModel* pModel = GetModel();
+
+ QList Rows;
+ for (int i = 0; i < pModel->rowCount(); ++i)
+ {
+ QModelIndex ModelIndex = pModel->index(i, 0);
+ RecursiveCopyPanel(ModelIndex, Rows);
+ }
+ FormatAndCopy(Rows);
+}
+
+void CPanelView::FormatAndCopy(QList Rows, bool Headder)
+{
+ int RowCount = Rows.length();
+
+ if (Headder)
+ {
+ Rows.prepend(QStringList());
+ Rows.prepend(CopyHeader());
+ Rows.prepend(QStringList());
+ }
+
+ QStringList TextRows;
+ if (m_SimpleFormat || !Headder)
+ {
+ foreach(const QStringList& Row, Rows)
+ TextRows.append(Row.join(m_CellSeparator));
+ }
+ else if(Rows.size() > (Headder ? 3 : 0))
+ {
+ int Columns = Rows[Headder ? 3 : 0].count();
+ QVector ColumnWidths(Columns, 0);
+
+ foreach(const QStringList& Row, Rows)
+ {
+ for (int i = 0; i < Min(Row.count(), Columns); i++)
+ {
+ int CellWidth = Row[i].length();
+ if (ColumnWidths[i] < CellWidth)
+ ColumnWidths[i] = CellWidth;
+ }
+ }
+
+ foreach(const QStringList& Row, Rows)
+ {
+ if (m_MaxCellWidth != 0 && RowCount > 1)
+ {
+ for (int Pos = 0;;Pos += m_MaxCellWidth)
+ {
+ bool More = false;
+
+ QString RowText;
+ for (int i = 0; i < Min(Row.count(), Columns); i++)
+ {
+ if (Row[i].length() > Pos)
+ RowText.append(Row[i].mid(Pos, m_MaxCellWidth).leftJustified(Min(m_MaxCellWidth, ColumnWidths[i]) + 3));
+ else
+ RowText.append(QString(Min(m_MaxCellWidth, ColumnWidths[i]) + 3, ' '));
+
+ if (Row[i].length() > Pos + m_MaxCellWidth)
+ More = true;
+ }
+ TextRows.append(RowText);
+
+ if (!More)
+ break;
+ }
+ }
+ else
+ {
+ QString RowText;
+ for (int i = 0; i < Min(Row.count(), Columns); i++)
+ RowText.append(Row[i].leftJustified(ColumnWidths[i] + 3));
+ TextRows.append(RowText);
+ }
+ }
+ }
+ QApplication::clipboard()->setText(TextRows.join("\n"));
+}
\ No newline at end of file
diff --git a/SandboxiePlus/MiscHelpers/Common/PanelView.h b/SandboxiePlus/MiscHelpers/Common/PanelView.h
new file mode 100644
index 00000000..2d1338f4
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/PanelView.h
@@ -0,0 +1,170 @@
+#pragma once
+
+#include "../mischelpers_global.h"
+
+class MISCHELPERS_EXPORT CPanelView : public QWidget
+{
+ Q_OBJECT
+public:
+ CPanelView(QWidget *parent = 0);
+ virtual ~CPanelView();
+
+ static void SetSimpleFormat(bool bSimple) { m_SimpleFormat = bSimple; }
+ static void SetMaxCellWidth(int iMaxWidth) { m_MaxCellWidth = iMaxWidth; }
+ static void SetCellSeparator(const QString& Sep) { m_CellSeparator = Sep; }
+
+protected slots:
+ virtual void OnMenu(const QPoint& Point);
+
+ virtual void OnCopyCell();
+ virtual void OnCopyRow();
+ virtual void OnCopyPanel();
+
+
+ virtual QTreeView* GetView() = 0;
+ virtual QAbstractItemModel* GetModel() = 0;
+ virtual QModelIndex MapToSource(const QModelIndex& Model) { return Model; }
+ static QModelIndexList MapToSource(QModelIndexList Indexes, QSortFilterProxyModel* pProxy) {
+ for (int i = 0; i < Indexes.count(); i++)
+ Indexes[i] = pProxy->mapToSource(Indexes[i]);
+ return Indexes;
+ }
+
+ virtual void AddPanelItemsToMenu(bool bAddSeparator = true);
+
+ virtual void ForceColumn(int column, bool bSet = true) { if (bSet) m_ForcedColumns.insert(column); else m_ForcedColumns.remove(column); }
+
+ virtual QStringList CopyHeader();
+ virtual QStringList CopyRow(const QModelIndex& ModelIndex, int Level = 0);
+ virtual void RecursiveCopyPanel(const QModelIndex& ModelIndex, QList& Rows, int Level = 0);
+
+protected:
+ void FormatAndCopy(QList Rows, bool Headder = true);
+
+ QMenu* m_pMenu;
+
+ QAction* m_pCopyCell;
+ QAction* m_pCopyRow;
+ QAction* m_pCopyPanel;
+
+ //bool m_CopyAll;
+ QSet m_ForcedColumns;
+ static bool m_SimpleFormat;
+ static int m_MaxCellWidth;
+ static QString m_CellSeparator;
+};
+
+template
+class CPanelWidget : public CPanelView
+{
+public:
+ CPanelWidget(QWidget *parent = 0) : CPanelView(parent)
+ {
+ m_pMainLayout = new QVBoxLayout();
+ m_pMainLayout->setMargin(0);
+ this->setLayout(m_pMainLayout);
+
+ m_pTreeList = new T();
+ m_pTreeList->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(m_pTreeList, SIGNAL(customContextMenuRequested( const QPoint& )), this, SLOT(OnMenu(const QPoint &)));
+ m_pMainLayout->addWidget(m_pTreeList);
+ m_pTreeList->setMinimumHeight(50);
+ AddPanelItemsToMenu();
+
+ m_pLastAction = m_pMenu->actions()[0];
+ }
+
+ virtual QMenu* GetMenu() { return m_pMenu; }
+ virtual void AddAction(QAction* pAction) { m_pMenu->insertAction(m_pLastAction, pAction); }
+
+ virtual T* GetTree() { return m_pTreeList; }
+ virtual QTreeView* GetView() { return m_pTreeList; }
+ virtual QAbstractItemModel* GetModel() { return m_pTreeList->model(); }
+
+protected:
+ QVBoxLayout* m_pMainLayout;
+
+ T* m_pTreeList;
+
+ QAction* m_pLastAction;
+};
+
+#include "TreeWidgetEx.h"
+#include "Finder.h"
+
+class MISCHELPERS_EXPORT CPanelWidgetEx : public CPanelWidget
+{
+ Q_OBJECT
+
+public:
+ CPanelWidgetEx(QWidget *parent = 0) : CPanelWidget(parent)
+ {
+ m_pFinder = new CFinder(NULL, this, false);
+ m_pMainLayout->addWidget(m_pFinder);
+ QObject::connect(m_pFinder, SIGNAL(SetFilter(const QRegExp&, bool, int)), this, SLOT(SetFilter(const QRegExp&, bool, int)));
+ }
+
+ static void ApplyFilter(QTreeWidgetEx* pTree, QTreeWidgetItem* pItem, const QRegExp& Exp/*, bool bHighLight = false, int Col = -1*/)
+ {
+ for (int j = 0; j < pTree->columnCount(); j++)
+ pItem->setBackground(j, !Exp.isEmpty() && pItem->text(j).contains(Exp) ? Qt::yellow : Qt::white);
+
+ for (int i = 0; i < pItem->childCount(); i++)
+ {
+ ApplyFilter(pTree, pItem->child(i), Exp/*, bHighLight, Col*/);
+ }
+ }
+
+ static void ApplyFilter(QTreeWidgetEx* pTree, const QRegExp& Exp/*, bool bHighLight = false, int Col = -1*/)
+ {
+ for (int i = 0; i < pTree->topLevelItemCount(); i++)
+ ApplyFilter(pTree, pTree->topLevelItem(i), Exp/*, bHighLight, Col*/);
+ }
+
+private slots:
+ void SetFilter(const QRegExp& Exp, bool bHighLight = false, int Col = -1) // -1 = any
+ {
+ ApplyFilter(m_pTreeList, Exp);
+ }
+
+private:
+
+ CFinder* m_pFinder;
+};
+
+#include "TreeViewEx.h"
+
+template
+class CPanelViewImpl: public CPanelWidget
+{
+public:
+ CPanelViewImpl(T* pModel, QWidget *parent = 0) : CPanelWidget(parent)
+ {
+ m_pModel = pModel;
+
+ m_pSortProxy = new CSortFilterProxyModel(false, this);
+ m_pSortProxy->setSortRole(Qt::EditRole);
+ m_pSortProxy->setSourceModel(m_pModel);
+ m_pSortProxy->setDynamicSortFilter(true);
+
+ m_pTreeList->setModel(m_pSortProxy);
+
+ m_pTreeList->setSelectionMode(QAbstractItemView::ExtendedSelection);
+#ifdef WIN32
+ QStyle* pStyle = QStyleFactory::create("windows");
+ m_pTreeList->setStyle(pStyle);
+#endif
+ m_pTreeList->setSortingEnabled(true);
+
+ m_pTreeList->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(m_pTreeList, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(OnMenu(const QPoint &)));
+
+ m_pTreeList->setColumnReset(1);
+ //connect(m_pTreeList, SIGNAL(ResetColumns()), m_pTreeList, SLOT(OnResetColumns()));
+ //connect(m_pBoxTree, SIGNAL(ColumnChanged(int, bool)), this, SLOT(OnColumnsChanged()));
+ }
+
+protected:
+ T* m_pModel;
+ QSortFilterProxyModel* m_pSortProxy;
+};
\ No newline at end of file
diff --git a/SandboxiePlus/MiscHelpers/Common/ProgressDialog.h b/SandboxiePlus/MiscHelpers/Common/ProgressDialog.h
new file mode 100644
index 00000000..24a251b3
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/ProgressDialog.h
@@ -0,0 +1,117 @@
+#pragma once
+
+#include "../mischelpers_global.h"
+
+class MISCHELPERS_EXPORT CProgressDialog : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ CProgressDialog(const QString& Prompt, QWidget* parent = 0)
+ : QMainWindow(parent)
+ {
+ setWindowFlags(Qt::Tool);
+
+ m_pMainWidget = new QWidget();
+ m_pMainLayout = new QGridLayout(this);
+ m_pMainWidget->setLayout(m_pMainLayout);
+ this->setCentralWidget(m_pMainWidget);
+
+ m_pMessageLabel = new QLabel(Prompt);
+ m_pMessageLabel->setMinimumWidth(300);
+ m_pMessageLabel->setWordWrap(true);
+ m_pMainLayout->addWidget(m_pMessageLabel, 0, 0, 1, 1);
+
+ m_pProgressBar = new QProgressBar();
+ m_pProgressBar->setTextVisible(false);
+ m_pProgressBar->setMaximum(0);
+ m_pProgressBar->setMinimum(0);
+ m_pMainLayout->addWidget(m_pProgressBar, 1, 0, 1, 1);
+
+ m_pButtonBox = new QDialogButtonBox();
+ m_pButtonBox->setOrientation(Qt::Horizontal);
+ m_pButtonBox->setStandardButtons(QDialogButtonBox::Cancel);
+ m_pMainLayout->addWidget(m_pButtonBox, 2, 0, 1, 1);
+
+ setFixedSize(sizeHint());
+
+ connect(m_pButtonBox,SIGNAL(rejected()),this,SIGNAL(Cancel()));
+
+ m_TimerId = startTimer(1000);
+ m_CountDown = 0;
+ }
+ ~CProgressDialog()
+ {
+ killTimer(m_TimerId);
+ }
+
+signals:
+ void Cancel();
+
+public slots:
+ void OnProgressMessage(const QString& Message, int Progress = -1)
+ {
+ if(!Message.isEmpty())
+ m_pMessageLabel->setText(Message);
+
+ if (Progress == -1)
+ {
+ if (m_pProgressBar->maximum() != 0)
+ m_pProgressBar->setMinimum(0);
+ }
+ else
+ {
+ if (m_pProgressBar->maximum() != 100)
+ m_pProgressBar->setMinimum(100);
+
+ m_pProgressBar->setValue(Progress);
+ }
+ }
+
+ void OnStatusMessage(const QString& Message, int Code = 0)
+ {
+ //if(Code == 0)
+ m_pMessageLabel->setText(Message);
+ //else // note: parent can't be set as this window may auto close
+ // QMessageBox::warning(NULL, this->windowTitle(), Message);
+ }
+
+ void OnFinished()
+ {
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setEnabled(false);
+ m_CountDown = 3;
+ }
+
+protected:
+ void timerEvent(QTimerEvent *e)
+ {
+ if (e->timerId() != m_TimerId)
+ {
+ QMainWindow::timerEvent(e);
+ return;
+ }
+
+ if(m_CountDown != 0)
+ {
+ m_CountDown--;
+ m_pButtonBox->button(QDialogButtonBox::Cancel)->setText(tr("Close (%1)").arg(m_CountDown));
+ if (m_CountDown == 0)
+ close();
+ }
+ }
+
+ void closeEvent(QCloseEvent *e)
+ {
+ emit Cancel();
+ this->deleteLater();
+ }
+
+ int m_TimerId;
+ int m_CountDown;
+
+ QWidget* m_pMainWidget;
+ QGridLayout* m_pMainLayout;
+ QLabel* m_pMessageLabel;
+ QProgressBar* m_pProgressBar;
+ QDialogButtonBox* m_pButtonBox;
+};
diff --git a/SandboxiePlus/MiscHelpers/Common/Settings.cpp b/SandboxiePlus/MiscHelpers/Common/Settings.cpp
new file mode 100644
index 00000000..a1eb4960
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/Settings.cpp
@@ -0,0 +1,113 @@
+#include "stdafx.h"
+#include "Settings.h"
+//#include "qzlib.h"
+#include "Common.h"
+
+bool TestWriteRight(const QString& Path)
+{
+ QFile TestFile(Path + "/~test-" + GetRand64Str() + ".tmp");
+ if(!TestFile.open(QFile::WriteOnly))
+ return false;
+ TestFile.close();
+ return TestFile.remove();
+}
+
+CSettings::CSettings(const QString& AppName, QMap DefaultValues, QObject* qObject) : QObject(qObject)
+{
+ m_ConfigDir = QCoreApplication::applicationDirPath();
+ if (!(m_bPortable = QFile::exists(m_ConfigDir + "/" + AppName + ".ini")))
+ {
+ QStringList dirs = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
+ if (dirs.isEmpty())
+ m_ConfigDir = QDir::homePath() + "/." + AppName;
+ else
+ m_ConfigDir = dirs.first() + "/" + AppName;
+ QDir().mkpath(m_ConfigDir);
+ }
+
+ m_pConf = new QSettings(m_ConfigDir + "/" + AppName + ".ini", QSettings::IniFormat, this);
+
+ m_pConf->sync();
+
+ m_DefaultValues = DefaultValues;
+ foreach (const QString& Key, m_DefaultValues.uniqueKeys())
+ {
+ const SSetting& Setting = m_DefaultValues[Key];
+ if(!m_pConf->contains(Key) || !Setting.Check(m_pConf->value(Key)))
+ {
+ if(Setting.IsBlob())
+ m_pConf->setValue(Key, Setting.Value.toByteArray().toBase64().replace("+","-").replace("/","_").replace("=",""));
+ else
+ m_pConf->setValue(Key, Setting.Value);
+ }
+ }
+}
+
+CSettings::~CSettings()
+{
+ m_pConf->sync();
+}
+
+bool CSettings::SetValue(const QString &key, const QVariant &value)
+{
+ QMutexLocker Locker(&m_Mutex);
+
+ if (!m_DefaultValues.isEmpty())
+ {
+ ASSERT(m_pConf->contains(key));
+#ifndef _DEBUG
+ if (!m_DefaultValues[key].Check(value))
+ return false;
+#endif
+ }
+
+ m_pConf->setValue(key, value);
+
+ m_ValueCache.clear();
+ return true;
+}
+
+QVariant CSettings::GetValue(const QString &key, const QVariant& preset)
+{
+ QMutexLocker Locker(&m_Mutex);
+
+ ASSERT(m_DefaultValues.isEmpty() || m_pConf->contains(key));
+
+ return m_pConf->value(key, preset);
+}
+
+void CSettings::SetBlob(const QString& key, const QByteArray& value)
+{
+ QString str;
+ //QByteArray data = Pack(value);
+ //if(data.size() < value.size())
+ // str = ":PackedArray:" + data.toBase64().replace("+","-").replace("/","_").replace("=","");
+ //else
+ str = ":ByteArray:" + value.toBase64().replace("+","-").replace("/","_").replace("=","");
+ SetValue(key, str);
+}
+
+QByteArray CSettings::GetBlob(const QString& key)
+{
+ QByteArray value;
+ QByteArray str = GetValue(key).toByteArray();
+ if(str.left(11) == ":ByteArray:")
+ value = QByteArray::fromBase64(str.mid(11).replace("-","+").replace("_","/"));
+ //else if(str.left(13) == ":PackedArray:")
+ // value = Unpack(QByteArray::fromBase64(str.mid(13).replace("-","+").replace("_","/")));
+ return value;
+}
+
+const QStringList CSettings::ListKeys(const QString& Root)
+{
+ QMutexLocker Locker(&m_Mutex);
+ QStringList Keys;
+ foreach(const QString& Key, m_pConf->allKeys())
+ {
+ QStringList Path = Key.split("/");
+ ASSERT(Path.count() == 2);
+ if(Path[0] == Root)
+ Keys.append(Path[1]);
+ }
+ return Keys;
+}
diff --git a/SandboxiePlus/MiscHelpers/Common/Settings.h b/SandboxiePlus/MiscHelpers/Common/Settings.h
new file mode 100644
index 00000000..72e2bc29
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/Settings.h
@@ -0,0 +1,150 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+
+#include "../mischelpers_global.h"
+
+class MISCHELPERS_EXPORT CSettings: public QObject
+{
+ Q_OBJECT
+
+public:
+ struct SStrRef
+ {
+ SStrRef(const char* pRef)
+ : Ref(pRef) {}
+
+ bool operator < (const SStrRef& Other) const {return strcmp(Ref, Other.Ref) < 0;}
+ bool operator == (const SStrRef& Other) const {return strcmp(Ref, Other.Ref) == 0;}
+
+ operator QString() const {return QString(Ref);}
+
+ const char* Ref;
+ };
+
+ union SCacheVal
+ {
+ bool _Bool;
+ qint32 _Int;
+ quint32 _UInt;
+ quint64 _UInt64;
+ };
+
+ struct SSetting
+ {
+ SSetting(){
+ MinValue = 0;
+ MaxValue = 0;
+ }
+
+ SSetting(const QVariant& value){
+ Value = value;
+ MinValue = 0;
+ MaxValue = 0;
+ }
+
+ SSetting(qint64 value, qint64 minvalue = LLONG_MIN, qint64 maxvalue = LLONG_MAX) {
+ Value = value;
+ MinValue = minvalue;
+ MaxValue = maxvalue;
+ }
+
+ SSetting(const QString& value, const QStringList& values) {
+ Value = value;
+ MinValue = 0;
+ MaxValue = 0;
+ Values = values;
+ }
+
+ bool Check(const QVariant& value) const
+ {
+ if(MinValue != MaxValue)
+ {
+ if(!value.canConvert(QVariant::LongLong))
+ return false;
+ qint64 Test = value.toLongLong();
+ if(Test < MinValue || MinValue > MaxValue)
+ return false;
+ }
+ else if(!Values.isEmpty())
+ {
+ if(!value.canConvert(QVariant::String))
+ return false;
+ if(!Values.contains(value.toString()))
+ return false;
+ }
+ return true;
+ }
+
+ virtual bool IsBlob() const {return false;}
+
+ QVariant Value;
+ qint64 MinValue;
+ qint64 MaxValue;
+ QStringList Values;
+ };
+
+ struct SBlobSetting: SSetting
+ {
+ SBlobSetting(const QByteArray& value){
+ Value = value;
+ MinValue = 0;
+ MaxValue = 0;
+ }
+ virtual bool IsBlob() const {return true;}
+ };
+
+ CSettings(const QString& AppName, QMap DefaultValues = QMap(), QObject* qObject = NULL);
+ virtual ~CSettings();
+
+ bool SetValue(const QString& key, const QVariant& value);
+ QVariant GetValue(const QString& key, const QVariant& preset = QVariant());
+
+ void SetBlob(const QString& key, const QByteArray& value);
+ QByteArray GetBlob(const QString& key);
+
+#define IMPL_CFG_CACHE_GET(x,y,z) \
+ x Get##y(const SStrRef& key, x def = 0) \
+ { \
+ QMutexLocker Locker(&m_Mutex); \
+ QMap::Iterator I = m_ValueCache.find(key); \
+ if(I != m_ValueCache.end()) \
+ return I.value()._##y; \
+ Locker.unlock(); \
+ x val = GetValue(key, def).to##z(); \
+ Locker.relock(); \
+ SCacheVal entry; \
+ entry._##y = val; \
+ m_ValueCache.insert(key, entry); \
+ return val; \
+ }
+ IMPL_CFG_CACHE_GET(bool, Bool, Bool);
+ IMPL_CFG_CACHE_GET(qint32, Int, Int);
+ IMPL_CFG_CACHE_GET(quint32, UInt, UInt);
+ IMPL_CFG_CACHE_GET(quint64, UInt64, ULongLong);
+#undef IMPL_CFG_CACHE_GET
+
+ const QString GetString(const QString& key, const QVariant& preset = QVariant()) {return GetValue(key, preset).toString();}
+ const QStringList GetStringList(const QString& key, const QVariant& preset = QVariant()) {return GetValue(key, preset).toStringList();}
+
+ const QStringList ListSettings() {QMutexLocker Locker(&m_Mutex); return m_pConf->allKeys();}
+ const QStringList ListGroupes() {QMutexLocker Locker(&m_Mutex); return m_pConf->childGroups();}
+ const QStringList ListKeys(const QString& Root);
+
+ const QString GetConfigDir() {QMutexLocker Locker(&m_Mutex); return m_ConfigDir;}
+ const bool IsPortable() {QMutexLocker Locker(&m_Mutex); return m_bPortable;}
+
+protected:
+ QMutex m_Mutex;
+ QMap m_DefaultValues;
+
+ QMapm_ValueCache;
+
+ QString m_ConfigDir;
+ bool m_bPortable;
+
+ QSettings* m_pConf;
+};
diff --git a/SandboxiePlus/MiscHelpers/Common/SettingsWidgets.cpp b/SandboxiePlus/MiscHelpers/Common/SettingsWidgets.cpp
new file mode 100644
index 00000000..79a5a43d
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/SettingsWidgets.cpp
@@ -0,0 +1,70 @@
+#include "stdafx.h"
+#include "SettingsWidgets.h"
+#include "CheckableMessageBox.h"
+
+///////////////////////////////////////////////////
+// CPathEdit
+
+CPathEdit::CPathEdit(bool bDirs, QWidget *parent)
+ : CTxtEdit(parent)
+{
+ m_bDirs = bDirs;
+
+ QHBoxLayout* pLayout = new QHBoxLayout(this);
+ pLayout->setMargin(0);
+ m_pEdit = new QLineEdit(this);
+ connect(m_pEdit, SIGNAL(textChanged(const QString &)), this, SIGNAL(textChanged(const QString &)));
+ pLayout->addWidget(m_pEdit);
+ QPushButton* pButton = new QPushButton("...");
+ pButton->setMaximumWidth(25);
+ connect(pButton, SIGNAL(pressed()), this, SLOT(Browse()));
+ pLayout->addWidget(pButton);
+}
+
+void CPathEdit::Browse()
+{
+ QString FilePath = m_bDirs
+ ? QFileDialog::getExistingDirectory(this, tr("Select Directory"))
+ : QFileDialog::getOpenFileName(0, tr("Browse"), "", QString("Any File (*.*)"));
+ if(!FilePath.isEmpty())
+ SetText(FilePath);
+}
+
+///////////////////////////////////////////////////
+// CProxyEdit
+
+CProxyEdit::CProxyEdit(QWidget *parent)
+ : CTxtEdit(parent)
+{
+ QHBoxLayout* pLayout = new QHBoxLayout(this);
+ pLayout->setMargin(0);
+
+ m_pType = new QComboBox();
+ m_pType->addItem(QString("No"));
+ m_pType->addItem(QString("http"));
+ m_pType->addItem(QString("socks5"));
+ connect(m_pType, SIGNAL(activated(int)), this, SLOT(OnType(int)));
+ pLayout->addWidget(m_pType);
+
+ m_pEdit = new QLineEdit(this);
+ connect(m_pEdit, SIGNAL(textChanged(const QString &)), this, SIGNAL(textChanged(const QString &)));
+ pLayout->addWidget(m_pEdit);
+}
+
+void CProxyEdit::SetText(const QString& Text)
+{
+ QUrl Url(Text);
+ m_pType->setCurrentText(Url.scheme());
+ m_pEdit->setText(Text);
+}
+
+void CProxyEdit::OnType(int Index)
+{
+ if(Index == 0)
+ m_pEdit->setEnabled(false);
+ else
+ {
+ m_pEdit->setEnabled(true);
+ m_pEdit->setText(m_pType->currentText() + "://");
+ }
+}
diff --git a/SandboxiePlus/MiscHelpers/Common/SettingsWidgets.h b/SandboxiePlus/MiscHelpers/Common/SettingsWidgets.h
new file mode 100644
index 00000000..a014050f
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/SettingsWidgets.h
@@ -0,0 +1,421 @@
+#pragma once
+
+#include "../mischelpers_global.h"
+
+class MISCHELPERS_EXPORT CMultiLineEdit: public QPlainTextEdit
+{
+ Q_OBJECT
+public:
+ CMultiLineEdit(QWidget *parent = 0)
+ :QPlainTextEdit(parent) {}
+
+ void SetLines(const QStringList& Lines){
+ setPlainText(Lines.join("\r\n"));
+ }
+ QStringList GetLines(){
+ return toPlainText().split(QRegExp("\r?\n"));
+ }
+};
+
+///////////////////////////////////////////////////
+//
+
+class MISCHELPERS_EXPORT QNumEdit: public QLineEdit
+{
+ Q_OBJECT
+
+ Q_PROPERTY(int Value READ GetValue WRITE SetValue)
+
+public:
+ QNumEdit(QWidget *parent = 0) : QLineEdit(parent) {}
+
+ virtual void SetValue(qint64 Value) = 0;
+ virtual qint64 GetValue() = 0;
+};
+
+ ///////////////////////////////////////////////////
+//
+
+class MISCHELPERS_EXPORT CKbpsEdit: public QNumEdit
+{
+ Q_OBJECT
+
+public:
+ CKbpsEdit(qint64 Factor = 1024, QWidget *parent = 0)
+ :QNumEdit(parent) , m_Factor(Factor) {}
+
+ void SetValue(qint64 Value){
+ if(Value <= 0)
+ setText(tr("Unlimited"));
+ else
+ setText(QString::number(Value/m_Factor));
+ }
+ qint64 GetValue(){
+ int Value = text().toInt();
+ if(Value > 0)
+ return Value*m_Factor;
+ else
+ return -1; // Unlimited;
+ }
+
+protected:
+ qint64 m_Factor;
+};
+
+///////////////////////////////////////////////////
+//
+
+class MISCHELPERS_EXPORT CFactorEdit: public QNumEdit
+{
+ Q_OBJECT
+
+public:
+ CFactorEdit(double Factor = 100.0, int Prec = 2, bool bEmpty = false, QWidget *parent = 0)
+ :QNumEdit(parent), m_Factor(Factor), m_Empty(bEmpty), m_Prec(Prec) {}
+
+ void SetValue(qint64 Value){
+ if(!Value && m_Empty)
+ setText("");
+ else
+ setText(QString::number(Value / m_Factor, 'f', m_Prec));
+ }
+ qint64 GetValue(){
+ return text().toDouble() * m_Factor;
+ }
+
+protected:
+ double m_Factor;
+ bool m_Empty;
+ int m_Prec;
+};
+
+///////////////////////////////////////////////////
+//
+
+class MISCHELPERS_EXPORT CSpinBoxEx: public QSpinBox
+{
+ Q_OBJECT
+public:
+ CSpinBoxEx(quint64 Unit, QWidget *parent = 0)
+ :QSpinBox(parent) {
+ m_Unit = Unit;
+ setSingleStep(Unit);
+ setMaximum(INT_MAX);
+ }
+
+protected:
+ virtual int valueFromText(const QString &text) const{
+ return text.toInt() * m_Unit;
+ }
+ virtual QString textFromValue(int val) const{
+ return QString::number(val / m_Unit);
+ }
+
+ quint64 m_Unit;
+};
+
+///////////////////////////////////////////////////
+//
+
+class MISCHELPERS_EXPORT CTxtEdit: public QWidget
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QString Data READ GetText WRITE SetText)
+
+public:
+ CTxtEdit(QWidget *parent = 0) : QWidget(parent) {}
+
+ virtual void SetText(const QString& Text) = 0;
+ virtual QString GetText() = 0;
+};
+
+///////////////////////////////////////////////////
+// CPathEdit
+
+class MISCHELPERS_EXPORT CPathEdit: public CTxtEdit
+{
+ Q_OBJECT
+public:
+ CPathEdit(bool bDirs = false, QWidget *parent = 0);
+
+ QLineEdit* GetEdit() {return m_pEdit;}
+
+ void SetText(const QString& Text) {m_pEdit->setText(Text);}
+ QString GetText() {return m_pEdit->text();}
+
+signals:
+ void textChanged(const QString& text);
+
+private slots:
+ void Browse();
+protected:
+ QLineEdit* m_pEdit;
+ bool m_bDirs;
+};
+
+///////////////////////////////////////////////////
+// CProxyEdit
+
+class MISCHELPERS_EXPORT CProxyEdit: public CTxtEdit
+{
+ Q_OBJECT
+
+public:
+ CProxyEdit(QWidget *parent = 0);
+
+ void SetText(const QString& Text);
+ QString GetText() {return m_pEdit->text();}
+
+signals:
+ void textChanged(const QString& text);
+
+private slots:
+ void OnType(int Index);
+
+protected:
+ QComboBox* m_pType;
+ QLineEdit* m_pEdit;
+};
+
+///////////////////////////////////////////////////
+//
+
+class MISCHELPERS_EXPORT QComboBoxEx: public QComboBox
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QVariant Data READ GetData)
+
+public:
+ QComboBoxEx(QWidget* pParent = 0) : QComboBox(pParent) {}
+
+ QVariant GetData() {return itemData(currentIndex());}
+};
+
+///////////////////////////////////////////////////
+//
+
+class MISCHELPERS_EXPORT QCheckBoxEx: public QCheckBox
+{
+ Q_OBJECT
+public:
+ QCheckBoxEx(const QString& Text, bool bSwap = true) : QCheckBox(Text)
+ {
+ m_bSwap = bSwap;
+ setTristate();
+ }
+
+ void SetState(int State)
+ {
+ if(m_bSwap)
+ {
+ switch(State)
+ {
+ case 0: State = Qt::PartiallyChecked; break;
+ case 1: State = Qt::Checked; break;
+ case 2: State = Qt::Unchecked; break;
+ }
+ }
+ setCheckState((Qt::CheckState)State);
+ }
+
+ int GetState()
+ {
+ int State = checkState();
+ if(m_bSwap)
+ {
+ switch(State)
+ {
+ case Qt::PartiallyChecked: State = 0; break;
+ case Qt::Checked: State = 1; break;
+ case Qt::Unchecked: State = 2; break;
+ }
+ }
+ return State;
+ }
+
+protected:
+ bool m_bSwap;
+};
+
+///////////////////////////////////////////////////
+//
+
+class MISCHELPERS_EXPORT QSecretCheckBox: public QCheckBoxEx
+{
+ Q_OBJECT
+public:
+ QSecretCheckBox(const QString& Text) : QCheckBoxEx(Text, false)
+ {
+ setTristate(false);
+ }
+
+protected:
+ void mouseDoubleClickEvent (QMouseEvent * e)
+ {
+ if(QApplication::keyboardModifiers() & Qt::ControlModifier)
+ setTristate();
+ }
+};
+
+///////////////////////////////////////////////////
+// QSpinBoxEx
+
+class MISCHELPERS_EXPORT QSpinBoxEx: public QSpinBox
+{
+public:
+ QSpinBoxEx(QWidget* parent, const QString& Default, const QString& Suffix = "", bool exponential = false) : QSpinBox(parent)
+ {
+ m_default = 1;
+
+ setRange(0, INT_MAX);
+ setSuffix(Suffix);
+ setSpecialValueText(Default + Suffix);
+ m_exponential = exponential;
+ }
+
+ void stepBy(int steps)
+ {
+ if(m_exponential)
+ {
+ int Value = value();
+ if(!Value)
+ {
+ Value = m_default;
+ if(Value <= 0)
+ Value = 1;
+ }
+
+ if(steps == 1)
+ setValue(Value*2);
+ else if(steps == -1)
+ setValue(Value/2);
+ else
+ QSpinBox::stepBy(steps);
+ }
+ else
+ QSpinBox::stepBy(steps);
+ }
+
+ void setDefault(int value) {m_default = value;}
+
+protected:
+ int m_default;
+ bool m_exponential;
+};
+
+
+///////////////////////////////////////////////////
+// CMenuAction & CActionWidget
+
+class MISCHELPERS_EXPORT QMenu_: public QMenu { public: void initStyleOption_(QStyleOptionMenuItem *option, const QAction *action) const {QMenu::initStyleOption(option, action);} };
+
+
+class MISCHELPERS_EXPORT CActionWidget: public QWidget
+{
+public:
+ CActionWidget(QWidget* parent = 0) : QWidget(parent) {}
+
+ void paintEvent(QPaintEvent* e)
+ {
+ QPainter p(this);
+
+ QMenu_ Menu;
+ QAction* action = new QAction(this);
+
+ QStyleOptionMenuItem opt;
+ Menu.initStyleOption_(&opt, action);
+ opt.menuRect = opt.rect = QRect(0, 0, 22, 22); // with this icons and text are drown properly
+ style()->drawControl(QStyle::CE_MenuItem, &opt, &p, this);
+ }
+};
+
+class MISCHELPERS_EXPORT CMenuAction: public QWidgetAction
+{
+public:
+ CMenuAction (QWidget* pControll, const QString& Title, QString IconFile = "") : QWidgetAction (pControll->parent())
+ {
+ QWidget* pWidget = new CActionWidget(pControll->parentWidget());
+ QHBoxLayout* pLayout = new QHBoxLayout();
+ pLayout->setMargin(0);
+
+ if(!IconFile.isEmpty())
+ {
+ QImage Image(IconFile);
+ QLabel* pIcon = new QLabel();
+ pIcon->setToolTip(Title);
+ pIcon->setPixmap(QPixmap::fromImage(Image).scaled(16,16, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
+ pLayout->addWidget(pIcon);
+
+ pLayout->insertSpacing(0, 3);
+ pLayout->insertSpacing(2, 6);
+ }
+ else
+ {
+ pLayout->addWidget(new QLabel(Title));
+
+ pLayout->insertSpacing(0, 32);
+ }
+
+ pLayout->addWidget(pControll);
+
+ pWidget->setLayout(pLayout);
+ setDefaultWidget(pWidget);
+ }
+};
+
+///////////////////////////////////////////////////
+// QCheckComboBox
+
+class MISCHELPERS_EXPORT QCheckComboBox : public QComboBox
+{
+ Q_OBJECT
+public:
+ QCheckComboBox()
+ {
+ view()->viewport()->installEventFilter(this);
+ }
+
+ virtual bool eventFilter(QObject *watched, QEvent *e)
+ {
+ switch (e->type())
+ {
+ case QEvent::MouseButtonPress:
+ break;
+ case QEvent::MouseButtonRelease:
+ QMouseEvent *m = static_cast(e);
+
+ // is check box rect pressed
+ if (m->localPos().x() < this->height())
+ {
+ QStandardItemModel *model_ = qobject_cast(model());
+ Qt::CheckState state = model_->data(view()->currentIndex(), Qt::CheckStateRole).value();
+
+ if (state != Qt::Checked)
+ state = Qt::Checked;
+ else
+ state = Qt::Unchecked;
+
+ model_->setData(view()->currentIndex(), state, Qt::CheckStateRole);
+
+ emit checkStateChanged(view()->currentIndex().row(), state);
+ return true;
+ }
+
+ /*if (isVisible() && view()->rect().contains(m->pos()) && view()->currentIndex().isValid()
+ && (view()->currentIndex().flags() & Qt::ItemIsEnabled)
+ && (view()->currentIndex().flags() & Qt::ItemIsSelectable)) {
+ this->hidePopup();
+ this->setCurrentIndex(view()->currentIndex().row());
+ return true;
+ }*/
+
+ break;
+ }
+ return false;
+ }
+
+signals:
+ void checkStateChanged(int Index, Qt::CheckState state);
+};
\ No newline at end of file
diff --git a/SandboxiePlus/MiscHelpers/Common/SmartGridWidget.cpp b/SandboxiePlus/MiscHelpers/Common/SmartGridWidget.cpp
new file mode 100644
index 00000000..2121fc6e
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/SmartGridWidget.cpp
@@ -0,0 +1,65 @@
+#include "stdafx.h"
+#include "SmartGridWidget.h"
+#include
+
+CSmartGridWidget::CSmartGridWidget(QWidget* parent)
+ : QWidget(parent)
+{
+ m_pMainLayout = new QGridLayout();
+ this->setLayout(m_pMainLayout);
+
+ m_pMainLayout->setMargin(1);
+ m_pMainLayout->setSpacing(2);
+
+ m_bReArangePending = false;
+}
+
+void CSmartGridWidget::SetBackground(const QColor& BackColor)
+{
+ QPalette pal = palette();
+ pal.setColor(QPalette::Background, BackColor);
+ this->setAutoFillBackground(true);
+ this->setPalette(pal);
+}
+
+void CSmartGridWidget::AddWidget(QWidget* pWidget)
+{
+ m_Widgets.append(pWidget);
+ if(!pWidget->parent())
+ pWidget->setParent(this);
+
+ if (!m_bReArangePending)
+ {
+ m_bReArangePending = true;
+ QTimer::singleShot(0, this, SLOT(ReArange()));
+ }
+}
+
+void CSmartGridWidget::ReArange()
+{
+ m_bReArangePending = false;
+
+ int count = 0;
+ while (count < m_Widgets.size())
+ {
+ if (m_Widgets[count] == NULL)
+ m_Widgets.removeAt(count);
+ else
+ count++;
+ }
+
+ float columns = ceil(sqrt((float)count));
+ float rows = columns > 0 ? ceil(count / columns) : 0;
+
+ for (int row = 0; row < rows; row++)
+ {
+ for (int column = 0; column < columns; column++)
+ {
+ int index = row * columns + column;
+ if (index >= count)
+ break;
+
+ m_pMainLayout->addWidget(m_Widgets.at(index), row, column);
+ }
+ }
+}
\ No newline at end of file
diff --git a/SandboxiePlus/MiscHelpers/Common/SmartGridWidget.h b/SandboxiePlus/MiscHelpers/Common/SmartGridWidget.h
new file mode 100644
index 00000000..2c328d1b
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/SmartGridWidget.h
@@ -0,0 +1,30 @@
+#pragma once
+#include
+
+#include "../mischelpers_global.h"
+
+class MISCHELPERS_EXPORT CSmartGridWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ CSmartGridWidget(QWidget* parent = NULL);
+ virtual ~CSmartGridWidget() {}
+
+ virtual void SetBackground(const QColor& BackColor);
+
+ virtual void AddWidget(QWidget* pWidget);
+
+ virtual int GetCount() { return m_Widgets.count(); }
+ virtual QWidget* GetWidget(int Index) { return m_Widgets.at(Index); }
+
+public slots:
+ virtual void ReArange();
+
+protected:
+ QGridLayout* m_pMainLayout;
+
+ QList>m_Widgets;
+
+ bool m_bReArangePending;
+};
diff --git a/SandboxiePlus/MiscHelpers/Common/SortFilterProxyModel.h b/SandboxiePlus/MiscHelpers/Common/SortFilterProxyModel.h
new file mode 100644
index 00000000..bd22d00b
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/SortFilterProxyModel.h
@@ -0,0 +1,82 @@
+#pragma once
+
+#include "../mischelpers_global.h"
+
+class MISCHELPERS_EXPORT CSortFilterProxyModel: public QSortFilterProxyModel
+{
+ Q_OBJECT
+
+public:
+ CSortFilterProxyModel(bool bAlternate, QObject* parrent = 0) : QSortFilterProxyModel(parrent)
+ {
+ m_bAlternate = bAlternate;
+ m_bHighLight = false;
+ }
+
+ bool filterAcceptsRow(int source_row, const QModelIndex & source_parent) const
+ {
+ if (m_bHighLight)
+ return true;
+
+ // allow the item to pass if any of the child items pass
+ if(!filterRegExp().isEmpty())
+ {
+ // get source-model index for current row
+ QModelIndex source_index = sourceModel()->index(source_row, 0, source_parent);
+ if(source_index.isValid())
+ {
+ // if any of children matches the filter, then current index matches the filter as well
+ int nb = sourceModel()->rowCount(source_index);
+ for(int i = 0; i < nb; i++)
+ {
+ if(filterAcceptsRow(i, source_index))
+ return true;
+ }
+ // check current index itself
+ return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
+ }
+ }
+
+ // default behavioure
+ return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
+ }
+
+ QVariant data(const QModelIndex &index, int role) const
+ {
+ QVariant Data = QSortFilterProxyModel::data(index, role);
+ if (role == Qt::BackgroundRole)
+ {
+ if (m_bHighLight)
+ {
+ if (!filterRegExp().isEmpty())
+ {
+ QString Key = QSortFilterProxyModel::data(index, filterRole()).toString();
+ if (Key.contains(filterRegExp()))
+ return QColor(Qt::yellow);
+ }
+ return QColor(Qt::white);
+ }
+
+ if (m_bAlternate && !Data.isValid())
+ {
+ if (0 == index.row() % 2)
+ return QColor(226, 237, 253);
+ else
+ return QColor(Qt::white);
+ }
+ }
+ return Data;
+ }
+
+public slots:
+ void SetFilter(const QRegExp& Exp, bool bHighLight = false, int Col = -1) // -1 = any
+ {
+ m_bHighLight = bHighLight;
+ setFilterKeyColumn(Col);
+ setFilterRegExp(Exp);
+ }
+
+protected:
+ bool m_bAlternate;
+ bool m_bHighLight;
+};
\ No newline at end of file
diff --git a/SandboxiePlus/MiscHelpers/Common/SplitTreeView.cpp b/SandboxiePlus/MiscHelpers/Common/SplitTreeView.cpp
new file mode 100644
index 00000000..8ac8efc3
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/SplitTreeView.cpp
@@ -0,0 +1,369 @@
+#include "stdafx.h"
+#include "SplitTreeView.h"
+
+CSplitTreeView::CSplitTreeView(QAbstractItemModel* pModel, QWidget *parent) : QWidget(parent)
+{
+ m_pModel = pModel;
+
+ m_pMainLayout = new QHBoxLayout();
+ m_pMainLayout->setMargin(0);
+ this->setLayout(m_pMainLayout);
+
+
+ m_pSplitter = new QSplitter();
+ m_pSplitter->setOrientation(Qt::Horizontal);
+ m_pMainLayout->addWidget(m_pSplitter);
+
+#ifdef WIN32
+ QStyle* pStyle = QStyleFactory::create("windows");
+#endif
+
+ m_LockSellection = 0;
+
+ // Tree
+ m_pTree = new QTreeView();
+
+ m_pOneModel = new COneColumnModel();
+ m_pOneModel->setSourceModel(m_pModel);
+ m_pTree->setModel(m_pOneModel);
+ //m_pTree->setModel(m_pSortProxy);
+
+ m_pTree->setSelectionMode(QAbstractItemView::ExtendedSelection);
+#ifdef WIN32
+ m_pTree->setStyle(pStyle);
+#endif
+ //m_pTree->setSortingEnabled(true);
+ m_pTree->setSortingEnabled(false);
+ m_pTree->setUniformRowHeights(true);
+ m_pTree->header()->setSortIndicatorShown(true);
+ m_pTree->header()->setSectionsClickable(true);
+ connect(m_pTree->header(), SIGNAL(sectionClicked(int)), this, SLOT(OnTreeCustomSortByColumn(int)));
+
+ m_pTree->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(m_pTree, SIGNAL(customContextMenuRequested( const QPoint& )), this, SIGNAL(MenuRequested(const QPoint &)));
+
+ m_pSplitter->addWidget(m_pTree);
+ //m_pSplitter->setCollapsible(0, false);
+ m_pSplitter->setStretchFactor(0, 0);
+ //
+
+
+ // List
+ // Note: It would be convinient to use QTreeViewEx here but qt does not scale well when there are too many columns
+ // hence we will add and remove columns at the model level directly.
+ // This way we can get out operational CPU usage to be quite comparable with TaskInfo na other advanced task managers
+ // Plus there are to many columns to cram them into one simple context menu :-)
+ //m_pList = new QTreeViewEx();
+ m_pList = new QTreeView();
+
+ m_pList->setModel(m_pModel);
+
+ m_pList->setSelectionMode(QAbstractItemView::ExtendedSelection);
+#ifdef WIN32
+ m_pList->setStyle(pStyle);
+#endif
+ m_pList->setSortingEnabled(true);
+ m_pList->setUniformRowHeights(true);
+ connect(m_pList->header(), SIGNAL(sectionClicked(int)), this, SLOT(OnListCustomSortByColumn(int)));
+
+ m_pList->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(m_pList, SIGNAL(customContextMenuRequested( const QPoint& )), this, SIGNAL(MenuRequested(const QPoint &)));
+
+ m_pSplitter->addWidget(m_pList);
+ m_pSplitter->setCollapsible(1, false);
+ m_pSplitter->setStretchFactor(1, 1);
+ //
+
+ connect(m_pSplitter, SIGNAL(splitterMoved(int,int)), this, SLOT(OnSplitterMoved(int,int)));
+
+ // Link selections
+ //m_pTree->setSelectionModel(m_pList->selectionModel()); // this works only when booth views share the same data model
+
+ connect(m_pTree->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(OnTreeSelectionChanged(QItemSelection,QItemSelection)));
+ connect(m_pList->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(OnListSelectionChanged(QItemSelection,QItemSelection)));
+
+ connect(m_pTree->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(OnTreeCurrentChanged(QModelIndex,QModelIndex)));
+ connect(m_pList->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(OnListCurrentChanged(QModelIndex,QModelIndex)));
+
+ m_bTreeEnabled = true;
+ m_pList->setColumnHidden(0, true);
+
+ // link expansion
+ connect(m_pTree, SIGNAL(expanded(const QModelIndex)), this, SLOT(OnExpandTree(const QModelIndex)));
+ connect(m_pTree, SIGNAL(collapsed(const QModelIndex)), this, SLOT(OnCollapseTree(const QModelIndex)));
+ //connect(m_pList, SIGNAL(expanded(const QModelIndex)), this, SLOT(expand(const QModelIndex)));
+ //connect(m_pList, SIGNAL(collapsed(const QModelIndex)), this, SLOT(collapse(const QModelIndex)));
+
+ // link scrollbars
+ m_pTree->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ m_pTree->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
+ m_pList->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
+ connect(m_pList->verticalScrollBar(), SIGNAL(valueChanged(int)), m_pTree->verticalScrollBar(), SLOT(setValue(int)));
+ connect(m_pTree->verticalScrollBar(), SIGNAL(valueChanged(int)), m_pList->verticalScrollBar(), SLOT(setValue(int)));
+
+ connect(m_pTree, SIGNAL(clicked(const QModelIndex&)), this, SLOT(OnClickedTree(const QModelIndex&)));
+ connect(m_pList, SIGNAL(clicked(const QModelIndex&)), this, SIGNAL(clicked(const QModelIndex&)));
+
+ connect(m_pTree, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(OnDoubleClickedTree(const QModelIndex&)));
+ connect(m_pList, SIGNAL(doubleClicked(const QModelIndex&)), this, SIGNAL(doubleClicked(const QModelIndex&)));
+
+ //QTimer::singleShot(0, this, [this]() {
+ // emit TreeEnabled(m_bTreeEnabled);
+ //});
+}
+
+CSplitTreeView::~CSplitTreeView()
+{
+
+}
+
+int CSplitTreeView::GetTreeWidth() const
+{
+ QList sizes = m_pSplitter->sizes();
+ return sizes[0];
+}
+
+void CSplitTreeView::SetTreeWidth(int Width)
+{
+ QList sizes = m_pSplitter->sizes();
+ int total = sizes[0] + sizes[1];
+ sizes[0] = Width;
+ sizes[1] = total - Width;
+}
+
+void CSplitTreeView::OnSplitterMoved(int pos, int index)
+{
+ if (index != 1)
+ return;
+
+ //if ((pos > 0) == m_bTreeEnabled)
+ // return;
+ //m_bTreeEnabled = (pos > 0);
+
+ m_pList->setColumnHidden(0, pos > 0);
+
+ //emit TreeEnabled(m_bTreeEnabled);
+}
+
+void CSplitTreeView::OnExpandTree(const QModelIndex& index)
+{
+ m_pList->expand(m_pOneModel->mapToSource(index));
+}
+
+void CSplitTreeView::OnCollapseTree(const QModelIndex& index)
+{
+ m_pList->collapse(m_pOneModel->mapToSource(index));
+}
+
+void CSplitTreeView::OnClickedTree(const QModelIndex& Index)
+{
+ emit clicked(m_pOneModel->mapToSource(Index));
+}
+
+void CSplitTreeView::OnDoubleClickedTree(const QModelIndex& Index)
+{
+ emit doubleClicked(m_pOneModel->mapToSource(Index));
+}
+
+void CSplitTreeView::expand(const QModelIndex &index)
+{
+ m_pTree->expand(m_pOneModel->mapFromSource(index));
+}
+
+void CSplitTreeView::collapse(const QModelIndex &index)
+{
+ m_pTree->collapse(m_pOneModel->mapFromSource(index));
+}
+
+void CSplitTreeView::OnTreeSelectionChanged(const QItemSelection& Selected, const QItemSelection& Deselected)
+{
+ if (m_LockSellection)
+ return;
+ m_LockSellection = 1;
+ QItemSelection SelectedItems;
+ foreach(const QModelIndex& Index, m_pTree->selectionModel()->selectedIndexes())
+ {
+ QModelIndex ModelIndex = m_pOneModel->mapToSource(Index);
+
+ QModelIndex ModelL = m_pModel->index(ModelIndex.row(), 0, ModelIndex.parent());
+ QModelIndex ModelR = m_pModel->index(ModelIndex.row(), m_pModel->columnCount()-1, ModelIndex.parent());
+
+ SelectedItems.append(QItemSelectionRange(ModelL, ModelR));
+ }
+ m_pList->selectionModel()->select(SelectedItems, QItemSelectionModel::ClearAndSelect);
+ /*
+ foreach(const QModelIndex& Index, Selected.indexes())
+ {
+ QModelIndex ModelIndex = m_pOneModel->mapToSource(Index);
+
+ QModelIndex ModelL = m_pModel->index(ModelIndex.row(), 0, ModelIndex.parent());
+ QModelIndex ModelR = m_pModel->index(ModelIndex.row(), m_pModel->columnCount()-1, ModelIndex.parent());
+
+ m_pList->selectionModel()->select(QItemSelection(ModelL, ModelR), QItemSelectionModel::Select);
+ }
+ foreach(const QModelIndex& Index, Deselected.indexes())
+ {
+ QModelIndex ModelIndex = m_pOneModel->mapToSource(Index);
+
+ QModelIndex ModelL = m_pModel->index(ModelIndex.row(), 0, ModelIndex.parent());
+ QModelIndex ModelR = m_pModel->index(ModelIndex.row(), m_pModel->columnCount()-1, ModelIndex.parent());
+
+ m_pList->selectionModel()->select(QItemSelection(ModelL, ModelR), QItemSelectionModel::Deselect);
+ }*/
+ m_LockSellection = 0;
+}
+
+void CSplitTreeView::OnListSelectionChanged(const QItemSelection& Selected, const QItemSelection& Deselected)
+{
+ if (m_LockSellection != 2)
+ emit selectionChanged(Selected, Deselected);
+
+ if (m_LockSellection)
+ return;
+ m_LockSellection = 1;
+ QItemSelection SelectedItems;
+ foreach(const QModelIndex& Index, m_pList->selectionModel()->selectedIndexes())
+ {
+ QModelIndex ModelIndex = m_pOneModel->mapFromSource(Index);
+ if (ModelIndex.column() != 0)
+ continue;
+
+ QModelIndex Model = m_pOneModel->index(ModelIndex.row(), 0, ModelIndex.parent());
+
+ SelectedItems.append(QItemSelectionRange(Model));
+ }
+ m_pTree->selectionModel()->select(SelectedItems, QItemSelectionModel::ClearAndSelect);
+ /*foreach(const QModelIndex& Index, Selected.indexes())
+ {
+ QModelIndex ModelIndex = m_pOneModel->mapFromSource(Index);
+ if (ModelIndex.column() != 0)
+ continue;
+ m_pTree->selectionModel()->select(ModelIndex, QItemSelectionModel::Select);
+ }
+ foreach(const QModelIndex& Index, Deselected.indexes())
+ {
+ QModelIndex ModelIndex = m_pOneModel->mapFromSource(Index);
+ if (ModelIndex.column() != 0)
+ continue;
+ m_pTree->selectionModel()->select(ModelIndex, QItemSelectionModel::Deselect);
+ }*/
+ m_LockSellection = 0;
+}
+
+void CSplitTreeView::OnTreeCurrentChanged(const QModelIndex ¤t, const QModelIndex &previous)
+{
+ if (m_LockSellection)
+ return;
+ m_LockSellection = 2;
+ int hPos = m_pList->horizontalScrollBar()->value(); // fix horizontalScrollBar position reset on selection
+ m_pList->selectionModel()->setCurrentIndex(m_pOneModel->mapToSource(current), QItemSelectionModel::SelectCurrent);
+ m_pList->horizontalScrollBar()->setValue(hPos);
+ m_LockSellection = 0;
+ emit currentChanged(m_pOneModel->mapToSource(current), m_pOneModel->mapToSource(previous));
+}
+
+void CSplitTreeView::OnListCurrentChanged(const QModelIndex ¤t, const QModelIndex &previous)
+{
+ if (m_LockSellection)
+ return;
+ m_LockSellection = 2;
+ m_pTree->selectionModel()->setCurrentIndex(m_pOneModel->mapFromSource(current), QItemSelectionModel::SelectCurrent);
+ m_LockSellection = 0;
+ emit currentChanged(current, previous);
+}
+
+QModelIndexList CSplitTreeView::selectedRows() const
+{
+ int Column = 0;
+ for (int i = 0; i < m_pModel->columnCount(); i++)
+ {
+ if (!m_pList->isColumnHidden(i))
+ {
+ Column = i;
+ break;
+ }
+ }
+
+ QModelIndexList IndexList;
+ foreach(const QModelIndex& Index, m_pList->selectionModel()->selectedIndexes())
+ {
+ if (Index.column() == Column)
+ IndexList.append(Index);
+ }
+ return IndexList;
+}
+
+void CSplitTreeView::SetTree(bool bSet)
+{
+ m_bTreeEnabled = bSet;
+ emit TreeEnabled(m_bTreeEnabled);
+}
+
+void CSplitTreeView::OnTreeCustomSortByColumn(int column)
+{
+ Qt::SortOrder order = m_pTree->header()->sortIndicatorOrder();
+ if (order == Qt::AscendingOrder)
+ SetTree(!m_bTreeEnabled);
+ m_pList->sortByColumn(column, order);
+ m_pTree->header()->setSortIndicatorShown(true);
+}
+
+void CSplitTreeView::OnListCustomSortByColumn(int column)
+{
+ Qt::SortOrder order = m_pList->header()->sortIndicatorOrder();
+ if (column == 0)
+ SetTree(!m_bTreeEnabled);
+ else if (m_bTreeEnabled)
+ SetTree(false);
+ m_pTree->header()->setSortIndicatorShown(false);
+}
+
+static const qint32 CSplitTreeViewMagic = 'STVs';
+
+QByteArray CSplitTreeView::saveState() const
+{
+ int version = 1;
+ QByteArray data;
+ QDataStream stream(&data, QIODevice::WriteOnly);
+
+ stream << qint32(CSplitTreeViewMagic);
+ stream << qint32(version);
+ stream << m_pList->header()->saveState();
+ stream << m_pSplitter->saveState();
+ stream << m_bTreeEnabled;
+
+ return data;
+}
+
+bool CSplitTreeView::restoreState(const QByteArray &state)
+{
+ int version = 1;
+ QByteArray sd = state;
+ QDataStream stream(&sd, QIODevice::ReadOnly);
+
+ qint32 marker;
+ stream >> marker;
+ qint32 v;
+ stream >> v;
+ if (marker != CSplitTreeViewMagic || v > version)
+ return false;
+
+ QByteArray header;
+ stream >> header;
+ m_pList->header()->restoreState(header);
+
+ QByteArray splitter;
+ stream >> splitter;
+ m_pSplitter->restoreState(splitter);
+
+ stream >> m_bTreeEnabled;
+ emit TreeEnabled(m_bTreeEnabled);
+
+ m_pTree->header()->setSortIndicatorShown(m_pList->header()->sortIndicatorSection() == 0);
+
+ QList sizes = m_pSplitter->sizes();
+ for(int i=0; i < sizes.count(); i++)
+ OnSplitterMoved(sizes[i], i);
+
+ return true;
+}
diff --git a/SandboxiePlus/MiscHelpers/Common/SplitTreeView.h b/SandboxiePlus/MiscHelpers/Common/SplitTreeView.h
new file mode 100644
index 00000000..b2a16c67
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/SplitTreeView.h
@@ -0,0 +1,116 @@
+#pragma once
+#include
+
+#include "TreeViewEx.h"
+
+#include "../mischelpers_global.h"
+
+class MISCHELPERS_EXPORT COneColumnModel : public QIdentityProxyModel
+{
+ Q_OBJECT
+public:
+ COneColumnModel( QObject* parrent = 0) : QIdentityProxyModel(parrent) {}
+
+ int columnCount(const QModelIndex &parent = QModelIndex()) const { return 1; }
+};
+
+class MISCHELPERS_EXPORT CSplitTreeView : public QWidget
+{
+ Q_OBJECT
+public:
+ CSplitTreeView(QAbstractItemModel* pModel, QWidget *parent = 0);
+ virtual ~CSplitTreeView();
+
+ QTreeView* GetView() { return m_pList; }
+ QTreeView* GetTree() { return m_pTree; }
+
+ void SetTree(bool bSet);
+ bool IsTree() const { return m_bTreeEnabled; }
+
+ void SetTreeWidth(int Width);
+ int GetTreeWidth() const;
+
+ QModelIndex currentIndex() const { return m_pList->currentIndex(); }
+ QModelIndexList selectedRows() const;
+
+ QByteArray saveState() const;
+ bool restoreState(const QByteArray &state);
+
+ template
+ void StartUpdatingWidgets(T& OldMap, T& Map)
+ {
+ for(typename T::iterator I = Map.begin(); I != Map.end();)
+ {
+ if(I.value().first == NULL)
+ I = Map.erase(I);
+ else
+ {
+ OldMap.insert(I.key(), I.value());
+ I++;
+ }
+ }
+ }
+
+ template
+ void EndUpdatingWidgets(T& OldMap, T& Map)
+ {
+ for(typename T::iterator I = OldMap.begin(); I != OldMap.end(); I++)
+ {
+ Map.remove(I.key());
+ if(I.value().second.isValid())
+ m_pList->setIndexWidget(I.value().second, NULL);
+ }
+ }
+
+signals:
+ void TreeEnabled(bool bEnabled);
+
+ void MenuRequested(const QPoint &);
+
+ void clicked(const QModelIndex& Index);
+ void doubleClicked(const QModelIndex& Index);
+ void currentChanged(const QModelIndex ¤t, const QModelIndex &previous);
+ void selectionChanged(const QItemSelection& selected, const QItemSelection& seselected);
+
+public slots:
+ void hideColumn(int column) { m_pList->hideColumn(column); }
+ void showColumn(int column) { m_pList->showColumn(column); }
+ void expand(const QModelIndex &index);
+ void collapse(const QModelIndex &index);
+ void resizeColumnToContents(int column) { m_pList->resizeColumnToContents(column); }
+ void sortByColumn(int column) { m_pList->sortByColumn(column); }
+ void expandAll() { m_pTree->expandAll(); }
+ void collapseAll() { m_pTree->collapseAll(); }
+ void expandToDepth(int depth) { m_pTree->expandToDepth(depth); }
+
+private slots:
+ void OnSplitterMoved(int pos, int index);
+
+ void OnClickedTree(const QModelIndex& Index);
+ void OnDoubleClickedTree(const QModelIndex& Index);
+
+ void OnExpandTree(const QModelIndex& index);
+ void OnCollapseTree(const QModelIndex& index);
+
+ void OnTreeSelectionChanged(const QItemSelection& Selected, const QItemSelection& Deselected);
+ void OnListSelectionChanged(const QItemSelection& Selected, const QItemSelection& Deselected);
+
+ void OnTreeCurrentChanged(const QModelIndex ¤t, const QModelIndex &previous);
+ void OnListCurrentChanged(const QModelIndex ¤t, const QModelIndex &previous);
+
+ void OnTreeCustomSortByColumn(int column);
+ void OnListCustomSortByColumn(int column);
+
+private:
+
+ QHBoxLayout* m_pMainLayout;
+
+ bool m_bTreeEnabled;
+ QSplitter* m_pSplitter;
+ QTreeView* m_pTree;
+ QTreeView* m_pList;
+ QAbstractItemModel* m_pModel;
+ COneColumnModel* m_pOneModel;
+
+ int m_LockSellection;
+};
diff --git a/SandboxiePlus/MiscHelpers/Common/TabPanel.cpp b/SandboxiePlus/MiscHelpers/Common/TabPanel.cpp
new file mode 100644
index 00000000..2f280104
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/TabPanel.cpp
@@ -0,0 +1,76 @@
+#include "stdafx.h"
+#include "TabPanel.h"
+
+CTabPanel::CTabPanel(QWidget* parent)
+ : QWidget(parent)
+{
+ m_pMainLayout = new QVBoxLayout();
+ m_pMainLayout->setMargin(0);
+ this->setLayout(m_pMainLayout);
+
+#ifdef USE_QEXTWIDGETS
+ m_pTabs = new QTabWidgetEx();
+ m_pTabs->setMultiRow(true);
+#else
+ m_pTabs = new QTabWidget();
+#endif
+ m_pMainLayout->addWidget(m_pTabs);
+}
+
+CTabPanel::~CTabPanel()
+{
+}
+
+void CTabPanel::ShowTab(int Index, bool bShow)
+{
+ if (Index >= m_AllTabs.size())
+ return;
+
+ m_AllTabs[Index].bVisible = bShow;
+
+ int ActiveTab = 0;
+ QStringList VisibleTabs;
+
+ SaveTabs(ActiveTab, VisibleTabs);
+
+ RebuildTabs(ActiveTab, VisibleTabs);
+}
+
+void CTabPanel::AddTab(QWidget* pWidget, const QString& Name)
+{
+ STab Tab{Name, pWidget, true};
+ m_AllTabs.append(Tab);
+ m_pTabs->addTab(Tab.pWidget, Tab.Name);
+}
+
+void CTabPanel::SaveTabs(int& ActiveTab, QStringList& VisibleTabs)
+{
+ ActiveTab = 0;
+ VisibleTabs.clear();
+ for(int i=0; i < m_AllTabs.size(); i++)
+ {
+ STab& Tab = m_AllTabs[i];
+
+ VisibleTabs.append(QString::number(Tab.bVisible));
+ if (m_pTabs->currentWidget() == Tab.pWidget)
+ ActiveTab = i;
+ }
+}
+
+void CTabPanel::RebuildTabs(const int ActiveTab, const QStringList& VisibleTabs)
+{
+ m_pTabs->clear();
+ for(int i=0; i < m_AllTabs.size(); i++)
+ {
+ STab& Tab = m_AllTabs[i];
+
+ if (VisibleTabs.size() <= i || VisibleTabs[i].toInt() != 0)
+ {
+ Tab.bVisible = true;
+
+ m_pTabs->addTab(Tab.pWidget, Tab.Name);
+ if (i == ActiveTab)
+ m_pTabs->setCurrentWidget(Tab.pWidget);
+ }
+ }
+}
diff --git a/SandboxiePlus/MiscHelpers/Common/TabPanel.h b/SandboxiePlus/MiscHelpers/Common/TabPanel.h
new file mode 100644
index 00000000..eb0d85ba
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/TabPanel.h
@@ -0,0 +1,44 @@
+#pragma once
+#include
+#include "Settings.h"
+
+#ifdef USE_QEXTWIDGETS
+#include "../../qextwidgets/qtabwidgetex.h"
+#endif
+
+#include "../mischelpers_global.h"
+
+class MISCHELPERS_EXPORT CTabPanel : public QWidget
+{
+ Q_OBJECT
+public:
+ CTabPanel(QWidget* parent = 0);
+ virtual ~CTabPanel();
+
+ virtual int GetTabCount() { return m_AllTabs.size(); }
+ virtual QString GetTabLabel(int Index) { if (Index >= m_AllTabs.size()) return ""; return m_AllTabs[Index].Name; }
+ virtual void ShowTab(int Index, bool bShow);
+ virtual bool IsTabVisible(int Index) { if (Index >= m_AllTabs.size()) return ""; return m_AllTabs[Index].bVisible; }
+
+protected:
+ virtual void InitializeTabs() = 0;
+ virtual void AddTab(QWidget* pWidget, const QString& Name);
+ virtual void RebuildTabs(const int ActiveTab, const QStringList& VisibleTabs);
+ virtual void SaveTabs(int& ActiveTab, QStringList& VisibleTabs);
+
+ QVBoxLayout* m_pMainLayout;
+
+#ifdef USE_QEXTWIDGETS
+ QTabWidgetEx* m_pTabs;
+#else
+ QTabWidget* m_pTabs;
+#endif
+ struct STab
+ {
+ QString Name;
+ QWidget* pWidget;
+ bool bVisible;
+ };
+ QVector m_AllTabs;
+};
+
diff --git a/SandboxiePlus/MiscHelpers/Common/TreeItemModel.cpp b/SandboxiePlus/MiscHelpers/Common/TreeItemModel.cpp
new file mode 100644
index 00000000..cda3334d
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/TreeItemModel.cpp
@@ -0,0 +1,502 @@
+#include "stdafx.h"
+#include "TreeItemModel.h"
+
+#define FIRST_COLUMN 0
+
+bool CTreeItemModel::m_DarkMode = false;
+
+CTreeItemModel::CTreeItemModel(QObject *parent)
+: QAbstractItemModelEx(parent)
+{
+ m_bTree = true;
+ m_bUseIcons = false;
+ m_Root = NULL;
+}
+
+CTreeItemModel::~CTreeItemModel()
+{
+ delete m_Root;
+}
+
+CSimpleTreeModel::CSimpleTreeModel(QObject *parent)
+ : CTreeItemModel(parent)
+{
+ m_Root = MkNode(QVariant());
+}
+
+QList CSimpleTreeModel::MakePath(const QVariantMap& Cur, const QMap& List)
+{
+ QVariant ParentID = Cur["ParentID"];
+ QVariantMap Parent = List.value(ParentID);
+
+ QList Path;
+ if (!Parent.isEmpty())
+ {
+ Path = MakePath(Parent, List);
+ Path.append(ParentID);
+ }
+ return Path;
+}
+
+bool CSimpleTreeModel::TestPath(const QList& Path, const QVariantMap& Cur, const QMap& List, int Index)
+{
+ QVariant ParentID = Cur["ParentID"];
+ QVariantMap Parent = List.value(ParentID);
+
+ if (!Parent.isEmpty())
+ {
+ if(Index >= Path.size() || Path[Path.size() - Index - 1] != ParentID)
+ return false;
+
+ return TestPath(Path, Parent, List, Index + 1);
+ }
+
+ return Path.size() == Index;
+}
+
+void CSimpleTreeModel::Sync(const QMap& List)
+{
+ QMap, QList > New;
+ QHash Old = m_Map;
+
+ foreach (const QVariantMap& Cur, List)
+ {
+ QVariant ID = Cur["ID"];
+
+ QModelIndex Index;
+
+ STreeNode* pNode = static_cast(Old.value(ID));
+ if(!pNode || (m_bTree ? !TestPath(pNode->Path, Cur, List) : !pNode->Path.isEmpty()))
+ {
+ pNode = static_cast(MkNode(ID));
+ pNode->Values.resize(columnCount());
+ if(m_bTree)
+ pNode->Path = MakePath(Cur, List);
+ pNode->IsBold = Cur["IsBold"].toBool();
+ pNode->Icon = Cur["Icon"];
+ New[pNode->Path].append(pNode);
+ }
+ else
+ {
+ Old[ID] = NULL;
+ Index = Find(m_Root, pNode);
+ }
+
+ //if(Index.isValid()) // this is to slow, be more precise
+ // emit dataChanged(createIndex(Index.row(), 0, pNode), createIndex(Index.row(), columnCount()-1, pNode));
+
+ int Col = 0;
+ bool State = false;
+ bool Changed = false;
+
+ QVariantMap Values = Cur["Values"].toMap();
+ for(int section = FIRST_COLUMN; section < columnCount(); section++)
+ {
+ if (!m_Columns.contains(section))
+ continue; // ignore columns which are hidden
+
+ QVariant Value = Values[QString::number(section)];
+
+ STreeNode::SValue& ColValue = pNode->Values[section];
+
+ if (ColValue.Raw != Value)
+ {
+ Changed = true;
+ ColValue.Raw = Value;
+
+ //ColValue.Formated =
+ }
+
+ if(State != Changed)
+ {
+ if(State && Index.isValid())
+ emit dataChanged(createIndex(Index.row(), Col, pNode), createIndex(Index.row(), section-1, pNode));
+ State = Changed;
+ Col = section;
+ }
+ Changed = false;
+ }
+ if(State && Index.isValid())
+ emit dataChanged(createIndex(Index.row(), Col, pNode), createIndex(Index.row(), columnCount()-1, pNode));
+
+ }
+
+ CTreeItemModel::Sync(New, Old);
+}
+
+void CTreeItemModel::Sync(QMap, QList >& New, QHash& Old)
+{
+ Purge(m_Root, QModelIndex(), Old);
+
+ if(!New.isEmpty())
+ {
+ emit layoutAboutToBeChanged();
+
+ //foreach(const QString& Path, New.uniqueKeys())
+ for(QMap, QList >::const_iterator I = New.begin(); I != New.end(); I++)
+ Fill(m_Root, QModelIndex(), I.key(), 0, I.value(), I.key());
+
+ emit layoutChanged();
+ }
+
+ emit Updated();
+}
+
+/*void CTreeItemModel::CountItems()
+{
+ CountItems(m_Root);
+}
+
+int CTreeItemModel::CountItems(STreeNode* pRoot)
+{
+ if(pRoot->Children.isEmpty())
+ return 1;
+
+ int Counter = 0;
+ foreach(STreeNode* pChild, pRoot->Children)
+ Counter += CountItems(pChild);
+ //pRoot->AllChildren = Counter;
+ return Counter;
+}*/
+
+void CTreeItemModel::Purge(STreeNode* pParent, const QModelIndex &parent, QHash &Old)
+{
+ int Removed = 0;
+
+ int Begin = -1;
+ int End = -1;
+ for(int i = pParent->Children.count()-1; i >= -1; i--)
+ {
+ STreeNode* pNode = i >= 0 ? pNode = pParent->Children[i] : NULL;
+ if(pNode)
+ Purge(pNode, index(i, 0, parent), Old);
+
+ bool bRemove = false;
+ if(pNode && (pNode->ID.isNull() || (bRemove = Old.value(pNode->ID) != NULL)) && pNode->Children.isEmpty()) // remove it
+ {
+ //m_Map.remove(pNode->ID, pNode);
+ m_Map.remove(pNode->ID);
+ if(End == -1)
+ End = i;
+ }
+ else // keep it
+ {
+ if(bRemove)
+ {
+ ASSERT(!pNode->Children.isEmpty()); // we wanted to remove it but we have to keep it
+ //m_Map.remove(pNode->ID, pNode);
+ m_Map.remove(pNode->ID);
+ pNode->ID = QVariant();
+ pNode->Icon.clear();
+ }
+
+ if(End != -1) // remove whats to be removed at once
+ {
+ Begin = i + 1;
+
+ beginRemoveRows(parent, Begin, End);
+ //ASSERT(pParent->Children.count() > End);
+ for(int j = End; j >= Begin; j--)
+ {
+ pNode = pParent->Children.takeAt(j);
+ delete pNode;
+ Removed++;
+ }
+ endRemoveRows();
+
+ End = -1;
+ Begin = -1;
+ }
+ }
+ }
+
+ if(Removed > 0)
+ {
+ pParent->Aux.clear();
+ for (int i = pParent->Children.count() - 1; i >= 0; i--)
+ {
+ pParent->Children[i]->Row = i;
+ pParent->Aux.insert(pParent->Children[i]->ID, i);
+ }
+ }
+}
+
+void CTreeItemModel::Fill(STreeNode* pParent, const QModelIndex &parent, const QList& Paths, int PathsIndex, const QList& New, const QList& Path)
+{
+ if(Paths.size() > PathsIndex)
+ {
+ QVariant CurPath = Paths.at(PathsIndex);
+ STreeNode* pNode;
+ int i = pParent->Aux.value(CurPath, -1);
+ if(i != -1)
+ pNode = pParent->Children[i];
+ else
+ {
+ i = 0;
+ pNode = MkNode(QVariant());
+ pNode->Parent = pParent;
+ pNode->Values.resize(columnCount());
+
+ //int Count = pParent->Children.count();
+ //beginInsertRows(parent, Count, Count);
+ pParent->Aux.insert(pNode->ID, pParent->Children.size());
+ pNode->Row = pParent->Children.size();
+ pParent->Children.append(pNode);
+ //endInsertRows();
+ }
+ Fill(pNode, index(i, 0, parent), Paths, PathsIndex + 1, New, Path);
+ }
+ else
+ {
+ for(QList::const_iterator I = New.begin(); I != New.end(); I++)
+ {
+ STreeNode* pNode = *I;
+ ASSERT(pNode);
+ //ASSERT(!m_Map.contains(pNode->ID));
+ m_Map.insert(pNode->ID, pNode);
+ pNode->Parent = pParent;
+
+ //int Count = pParent->Children.count();
+ //beginInsertRows(parent, Count, Count);
+ pParent->Aux.insert(pNode->ID, pParent->Children.size());
+ pNode->Row = pParent->Children.size();
+ pParent->Children.append(pNode);
+ //endInsertRows();
+ }
+ }
+}
+
+QModelIndex CTreeItemModel::FindIndex(const QVariant& ID)
+{
+ if(STreeNode* pNode = m_Map.value(ID))
+ return Find(m_Root, pNode);
+ return QModelIndex();
+}
+
+QModelIndex CTreeItemModel::Find(STreeNode* pParent, STreeNode* pNode)
+{
+ // ''find''
+ ASSERT(pNode->Parent->Children[pNode->Row] == pNode);
+ return createIndex(pNode->Row, FIRST_COLUMN, pNode);
+ /*
+ int count = pParent->Children.count();
+ for(int i=0; i < count; i++)
+ {
+ if (pParent->Children[i] == pNode)
+ {
+ ASSERT(i == pNode->Row);
+ return createIndex(i, FIRST_COLUMN, pNode);
+ }
+
+ QModelIndex Index = Find(pParent->Children[i], pNode);
+ if(Index.isValid())
+ return Index;
+ }
+ return QModelIndex();
+ */
+}
+
+void CTreeItemModel::Clear()
+{
+ QHash Old = m_Map;
+ //beginResetModel();
+ Purge(m_Root, QModelIndex(), Old);
+ //endResetModel();
+ ASSERT(m_Map.isEmpty());
+}
+
+void CTreeItemModel::RemoveIndex(const QModelIndex &index)
+{
+ if (!index.isValid())
+ return;
+
+ STreeNode* pNode = static_cast(index.internalPointer());
+ ASSERT(pNode);
+
+ QHash Old;
+ Old[pNode->ID] = pNode;
+
+ Purge(m_Root, QModelIndex(), Old);
+}
+
+QVariant CTreeItemModel::data(const QModelIndex &index, int role) const
+{
+ return Data(index, role, index.column());
+}
+
+bool CTreeItemModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ if(index.column() == FIRST_COLUMN && role == Qt::CheckStateRole)
+ {
+ STreeNode* pNode = static_cast(index.internalPointer());
+ ASSERT(pNode);
+ emit CheckChanged(pNode->ID, value.toInt() != Qt::Unchecked);
+ return true;
+ }
+ return false;
+}
+
+QVariant CTreeItemModel::Data(const QModelIndex &index, int role, int section) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ //if(role == Qt::SizeHintRole)
+ // return QSize(64,16); // for fixing height
+
+ STreeNode* pNode = static_cast(index.internalPointer());
+ ASSERT(pNode);
+
+ if (pNode->Values.size() <= section)
+ return QVariant();
+
+ return NodeData(pNode, role, section);
+}
+
+QVariant CTreeItemModel::NodeData(STreeNode* pNode, int role, int section) const
+{
+ switch(role)
+ {
+ case Qt::DisplayRole:
+ {
+ STreeNode::SValue& Value = pNode->Values[section];
+ return Value.Formated.isValid() ? Value.Formated : Value.Raw;
+ }
+ case Qt::EditRole: // sort role
+ {
+ STreeNode::SValue& value = pNode->Values[section];
+ return value.SortKey.isValid() ? value.SortKey : value.Raw;
+ }
+ case Qt::ToolTipRole:
+ {
+ QString ToolTip;
+ emit ToolTipCallback(pNode->ID, ToolTip);
+ if(!ToolTip.isNull())
+ return ToolTip;
+ break;
+ }
+ case Qt::DecorationRole:
+ {
+ if (m_bUseIcons && section == FIRST_COLUMN)
+ return pNode->Icon.isValid() ? pNode->Icon : GetDefaultIcon();
+ break;
+ }
+ case Qt::FontRole:
+ {
+ if (section == FIRST_COLUMN && pNode->IsBold)
+ {
+ QFont fnt;
+ fnt.setBold(true);
+ return fnt;
+ }
+ break;
+ }
+ case Qt::BackgroundRole:
+ {
+ if(!m_DarkMode)
+ return pNode->Color.isValid() ? pNode->Color : QVariant();
+ break;
+ }
+ case Qt::ForegroundRole:
+ {
+ if(m_DarkMode)
+ return pNode->Color.isValid() ? pNode->Color : QVariant();
+ else if (pNode->IsGray)
+ {
+ QColor Color = Qt::darkGray;
+ return QBrush(Color);
+ }
+ break;
+ }
+ case Qt::CheckStateRole:
+ {
+ /*if(section == eModule)
+ {
+ if(pNode->...)
+ return Qt::Unchecked;
+ else
+ return Qt::Checked;
+ }*/
+ break;
+ }
+ case Qt::UserRole:
+ {
+ switch(section)
+ {
+ case FIRST_COLUMN: return pNode->ID;
+ }
+ break;
+ }
+ }
+ return QVariant();
+}
+
+Qt::ItemFlags CTreeItemModel::flags(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return 0;
+ if(index.column() == 0)
+ return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable;
+ return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
+}
+
+QModelIndex CTreeItemModel::index(int row, int column, const QModelIndex &parent) const
+{
+ if (!hasIndex(row, column, parent))
+ return QModelIndex();
+
+ STreeNode* pParent;
+ if (!parent.isValid())
+ pParent = m_Root;
+ else
+ pParent = static_cast(parent.internalPointer());
+
+ if(STreeNode* pNode = pParent->Children.count() > row ? pParent->Children[row] : NULL)
+ return createIndex(row, column, pNode);
+ return QModelIndex();
+}
+
+QModelIndex CTreeItemModel::parent(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return QModelIndex();
+
+ STreeNode* pNode = static_cast(index.internalPointer());
+ ASSERT(pNode->Parent);
+ STreeNode* pParent = pNode->Parent;
+ if (pParent == m_Root)
+ return QModelIndex();
+
+ int row = 0;
+ if(pParent->Parent)
+ row = pParent->Parent->Children.indexOf(pParent);
+ return createIndex(row, 0, pParent);
+}
+
+int CTreeItemModel::rowCount(const QModelIndex &parent) const
+{
+ if (parent.column() > 0)
+ return 0;
+
+ STreeNode* pNode;
+ if (!parent.isValid())
+ pNode = m_Root;
+ else
+ pNode = static_cast(parent.internalPointer());
+ return pNode->Children.count();
+}
+
+int CSimpleTreeModel::columnCount(const QModelIndex &parent) const
+{
+ return m_Headers.count();
+}
+
+QVariant CSimpleTreeModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
+ {
+ if (section < m_Headers.size())
+ return m_Headers.at(section);
+ }
+ return QVariant();
+}
\ No newline at end of file
diff --git a/SandboxiePlus/MiscHelpers/Common/TreeItemModel.h b/SandboxiePlus/MiscHelpers/Common/TreeItemModel.h
new file mode 100644
index 00000000..d77ce8e1
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/TreeItemModel.h
@@ -0,0 +1,123 @@
+#pragma once
+#include "TreeViewEx.h"
+
+#include "../mischelpers_global.h"
+
+class MISCHELPERS_EXPORT CTreeItemModel : public QAbstractItemModelEx
+{
+ Q_OBJECT
+
+public:
+ CTreeItemModel(QObject *parent = 0);
+ virtual ~CTreeItemModel();
+
+ void SetTree(bool bTree) { m_bTree = bTree; }
+ bool IsTree() const { return m_bTree; }
+ void SetUseIcons(bool bUseIcons) { m_bUseIcons = bUseIcons; }
+ static void SetDarkMode(bool bDark) { m_DarkMode = bDark;}
+
+ //void CountItems();
+ QModelIndex FindIndex(const QVariant& ID);
+ void RemoveIndex(const QModelIndex &index);
+
+ QVariant Data(const QModelIndex &index, int role, int section) const;
+
+ // derived functions
+ virtual QVariant data(const QModelIndex &index, int role) const;
+ virtual bool setData(const QModelIndex &index, const QVariant &value, int role);
+ virtual Qt::ItemFlags flags(const QModelIndex &index) const;
+ virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
+ virtual QModelIndex parent(const QModelIndex &index) const;
+ virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ virtual int columnCount(const QModelIndex &parent = QModelIndex()) const = 0;
+ virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const = 0;
+
+public slots:
+ void Clear();
+
+signals:
+ void CheckChanged(const QVariant& ID, bool State);
+ void ToolTipCallback(const QVariant& ID, QString& ToolTip) const;
+ void Updated();
+
+protected:
+ struct STreeNode
+ {
+ STreeNode(const QVariant& Id){
+ ID = Id;
+ Parent = NULL;
+ Row = 0;
+ //AllChildren = 0;
+
+ IsBold = false;
+ IsGray = false;
+ }
+ virtual ~STreeNode(){
+ foreach(STreeNode* pNode, Children)
+ delete pNode;
+ }
+
+ QVariant ID;
+
+ STreeNode* Parent;
+ int Row;
+ QList Path;
+ QList Children;
+ //int AllChildren;
+ QMap Aux;
+
+ QVariant Icon;
+ bool IsBold;
+ bool IsGray;
+ QColor Color;
+ struct SValue
+ {
+ QVariant Raw;
+ QVariant SortKey;
+ QVariant Formated;
+ };
+ QVector Values;
+ };
+
+ virtual QVariant NodeData(STreeNode* pNode, int role, int section) const;
+
+ virtual STreeNode* MkNode(const QVariant& Id) = 0; // { return new STreeNode(Id); }
+
+ void Sync(QMap, QList >& New, QHash& Old);
+ void Purge(STreeNode* pParent, const QModelIndex &parent, QHash& Old);
+ void Fill(STreeNode* pParent, const QModelIndex &parent, const QList& Paths, int PathsIndex, const QList& New, const QList& Path);
+ QModelIndex Find(STreeNode* pParent, STreeNode* pNode);
+ //int CountItems(STreeNode* pRoot);
+
+ virtual QVariant GetDefaultIcon() const { return QVariant(); }
+
+ STreeNode* m_Root;
+ QHash m_Map;
+ bool m_bTree;
+ bool m_bUseIcons;
+
+ static bool m_DarkMode;
+};
+
+class MISCHELPERS_EXPORT CSimpleTreeModel : public CTreeItemModel
+{
+ Q_OBJECT
+
+public:
+ CSimpleTreeModel(QObject *parent = 0);
+
+ void Sync(const QMap& List);
+
+ void setHeaderLabels(const QStringList& Columns) { m_Headers = Columns; }
+
+ virtual int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
+
+protected:
+ virtual STreeNode* MkNode(const QVariant& Id) { return new STreeNode(Id); }
+
+ QList MakePath(const QVariantMap& Cur, const QMap& List);
+ bool TestPath(const QList& Path, const QVariantMap& Cur, const QMap& List, int Index = 0);
+
+ QStringList m_Headers;
+};
\ No newline at end of file
diff --git a/SandboxiePlus/MiscHelpers/Common/TreeViewEx.h b/SandboxiePlus/MiscHelpers/Common/TreeViewEx.h
new file mode 100644
index 00000000..00de812d
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/TreeViewEx.h
@@ -0,0 +1,346 @@
+#pragma once
+
+#include
+
+__inline uint qHash( const QVariant & var )
+{
+ if ( !var.isValid() || var.isNull() )
+ //return -1;
+ Q_ASSERT(0);
+
+ switch ( var.type() )
+ {
+ case QVariant::Int:
+ return qHash( var.toInt() );
+ break;
+ case QVariant::UInt:
+ return qHash( var.toUInt() );
+ break;
+ case QVariant::Bool:
+ return qHash( var.toUInt() );
+ break;
+ case QVariant::Double:
+ return qHash( var.toUInt() );
+ break;
+ case QVariant::LongLong:
+ return qHash( var.toLongLong() );
+ break;
+ case QVariant::ULongLong:
+ return qHash( var.toULongLong() );
+ break;
+ case QVariant::String:
+ return qHash( var.toString() );
+ break;
+ case QVariant::Char:
+ return qHash( var.toChar() );
+ break;
+ case QVariant::StringList:
+ return qHash( var.toString() );
+ break;
+ case QVariant::ByteArray:
+ return qHash( var.toByteArray() );
+ break;
+ case QVariant::Date:
+ case QVariant::Time:
+ case QVariant::DateTime:
+ case QVariant::Url:
+ case QVariant::Locale:
+ case QVariant::RegExp:
+ return qHash( var.toString() );
+ break;
+ case QVariant::Map:
+ case QVariant::List:
+ case QVariant::BitArray:
+ case QVariant::Size:
+ case QVariant::SizeF:
+ case QVariant::Rect:
+ case QVariant::LineF:
+ case QVariant::Line:
+ case QVariant::RectF:
+ case QVariant::Point:
+ case QVariant::PointF:
+ // not supported yet
+ break;
+ case QVariant::UserType:
+ case QVariant::Invalid:
+ default:
+ return -1;
+ }
+
+ // could not generate a hash for the given variant
+ return -1;
+}
+
+#include "../mischelpers_global.h"
+
+class MISCHELPERS_EXPORT QAbstractItemModelEx : public QAbstractItemModel
+{
+ Q_OBJECT
+
+public:
+ QAbstractItemModelEx(QObject *parent = 0) : QAbstractItemModel(parent) {}
+ virtual ~QAbstractItemModelEx() {}
+
+ bool IsColumnEnabled(int column)
+ {
+ return m_Columns.contains(column);
+ }
+
+ void SetColumnEnabled(int column, bool set)
+ {
+ if (!set)
+ m_Columns.remove(column);
+ else
+ m_Columns.insert(column);
+ }
+
+protected:
+
+ QSet m_Columns;
+};
+
+class MISCHELPERS_EXPORT QTreeViewEx: public QTreeView
+{
+ Q_OBJECT
+public:
+ QTreeViewEx(QWidget *parent = 0) : QTreeView(parent)
+ {
+ setUniformRowHeights(true);
+
+ m_ColumnReset = 1;
+
+ header()->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(header(), SIGNAL(customContextMenuRequested( const QPoint& )), this, SLOT(OnMenuRequested(const QPoint &)));
+
+ m_pMenu = new QMenu(this);
+ }
+
+ void setColumnReset(int iMode)
+ {
+ m_ColumnReset = iMode;
+ }
+
+ void setColumnFixed(int column, bool fixed)
+ {
+ if (fixed)
+ m_FixedColumns.insert(column);
+ else
+ m_FixedColumns.remove(column);
+ }
+
+ bool isColumnFixed(int column) const
+ {
+ return m_FixedColumns.contains(column);
+ }
+
+ QModelIndexList selectedRows() const
+ {
+ int Column = 0;
+ QAbstractItemModel* pModel = model();
+ for (int i = 0; i < pModel->columnCount(); i++)
+ {
+ if (!isColumnHidden(i))
+ {
+ Column = i;
+ break;
+ }
+ }
+
+ QModelIndexList IndexList;
+ foreach(const QModelIndex& Index, selectedIndexes())
+ {
+ if (Index.column() == Column)
+ IndexList.append(Index);
+ }
+ return IndexList;
+ }
+
+ template
+ void StartUpdatingWidgets(T& OldMap, T& Map)
+ {
+ for(typename T::iterator I = Map.begin(); I != Map.end();)
+ {
+ if(I.value().first == NULL)
+ I = Map.erase(I);
+ else
+ {
+ OldMap.insert(I.key(), I.value());
+ I++;
+ }
+ }
+ }
+
+ template
+ void EndUpdatingWidgets(T& OldMap, T& Map)
+ {
+ for(typename T::iterator I = OldMap.begin(); I != OldMap.end(); I++)
+ {
+ Map.remove(I.key());
+ if(I.value().second.isValid())
+ setIndexWidget(I.value().second, NULL);
+ }
+ }
+
+ bool restoreState(const QByteArray &state)
+ {
+ bool bRet = header()->restoreState(state);
+
+ SyncColumnsWithModel();
+
+ return bRet;
+ }
+
+ QByteArray saveState() const
+ {
+ return header()->saveState();
+ }
+
+ QAbstractItemModelEx* modelEx() const
+ {
+ QAbstractItemModelEx* pModel = qobject_cast(model());
+ if (!pModel)
+ {
+ QSortFilterProxyModel* pProxyModel = qobject_cast(model());
+ if(pProxyModel)
+ pModel = qobject_cast(pProxyModel->sourceModel());
+ }
+ return pModel;
+ }
+
+ void SetColumnHidden(int column, bool hide, bool fixed = false)
+ {
+ if (!fixed && isColumnFixed(column))
+ return; // can not change fixed columns
+
+ setColumnHidden(column, hide);
+
+ if(QAbstractItemModelEx* pModel = modelEx())
+ pModel->SetColumnEnabled(column, !hide);
+
+ if (fixed)
+ setColumnFixed(column, true);
+
+ emit ColumnChanged(column, !hide);
+ }
+
+signals:
+ void ColumnChanged(int column, bool visible);
+ void ResetColumns();
+
+public slots:
+ void SyncColumnsWithModel()
+ {
+ if(QAbstractItemModelEx* pModel = modelEx())
+ {
+ for (int i = 0; i < pModel->columnCount(); i++)
+ pModel->SetColumnEnabled(i, !isColumnHidden(i));
+ }
+ }
+
+ void OnResetColumns()
+ {
+ QAbstractItemModel* pModel = model();
+ for (int i = 0; i < pModel->columnCount(); i++)
+ SetColumnHidden(i, false);
+ }
+
+private slots:
+ void OnMenuRequested(const QPoint &point)
+ {
+ QAbstractItemModel* pModel = model();
+
+ if(m_Columns.isEmpty())
+ {
+ for(int i=0; i < pModel->columnCount(); i++)
+ {
+ QString Label = pModel->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString();
+ if(Label.isEmpty() || m_FixedColumns.contains(i))
+ continue;
+ QAction* pAction = new QAction(Label, m_pMenu);
+ pAction->setCheckable(true);
+ pAction->setChecked(!isColumnHidden(i));
+ connect(pAction, SIGNAL(triggered()), this, SLOT(OnMenu()));
+ m_pMenu->addAction(pAction);
+ m_Columns[pAction] = i;
+ }
+
+ if (m_ColumnReset)
+ {
+ m_pMenu->addSeparator();
+ QAction* pAction = m_pMenu->addAction(tr("Reset columns"));
+ if(m_ColumnReset == 1)
+ connect(pAction, SIGNAL(triggered()), this, SLOT(OnResetColumns()));
+ else
+ connect(pAction, SIGNAL(triggered()), this, SIGNAL(ResetColumns()));
+ }
+ }
+
+ for(QMap::iterator I = m_Columns.begin(); I != m_Columns.end(); I++)
+ I.key()->setChecked(!isColumnHidden(I.value()));
+
+ m_pMenu->popup(QCursor::pos());
+ }
+
+ void OnMenu()
+ {
+ QAction* pAction = (QAction*)sender();
+ int Column = m_Columns.value(pAction, -1);
+ SetColumnHidden(Column, !pAction->isChecked());
+ }
+
+protected:
+ QMenu* m_pMenu;
+ QMap m_Columns;
+ QSet m_FixedColumns;
+ int m_ColumnReset;
+};
+
+class MISCHELPERS_EXPORT QStyledItemDelegateMaxH : public QStyledItemDelegate
+{
+ Q_OBJECT
+public:
+ QStyledItemDelegateMaxH(int MaxHeight, QObject *parent = 0)
+ : QStyledItemDelegate(parent) {m_MaxHeight = MaxHeight;}
+
+ QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
+ {
+ QSize size = QStyledItemDelegate::sizeHint(option, index);
+ size.setHeight(m_MaxHeight);
+ return size;
+ }
+
+ int m_MaxHeight;
+};
+
+class MISCHELPERS_EXPORT CStyledGridItemDelegate : public QStyledItemDelegateMaxH
+{
+public:
+ explicit CStyledGridItemDelegate(int MaxHeight, QObject * parent = 0) : CStyledGridItemDelegate(MaxHeight, false, parent) { }
+ explicit CStyledGridItemDelegate(int MaxHeight, QColor Color, QObject * parent = 0) : CStyledGridItemDelegate(MaxHeight, Color, false, parent) { }
+ explicit CStyledGridItemDelegate(int MaxHeight, bool Tree, QObject * parent = 0) : CStyledGridItemDelegate(MaxHeight, QColor(Qt::darkGray), false, parent) { }
+ explicit CStyledGridItemDelegate(int MaxHeight, QColor Color, bool Tree, QObject * parent = 0) : QStyledItemDelegateMaxH(MaxHeight, parent) {
+ m_Color = Color;
+ m_Tree = Tree;
+ m_Grid = true;
+ }
+
+ void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const
+ {
+ QStyledItemDelegate::paint(painter, option, index);
+
+ if (m_Grid)
+ {
+ painter->save();
+ painter->setPen(m_Color);
+ //painter->drawRect(option.rect);
+ //painter->drawLine(option.rect.left(), option.rect.top(), option.rect.right(), option.rect.top());
+ painter->drawLine(option.rect.right(), option.rect.top(), option.rect.right(), option.rect.bottom());
+ painter->drawLine(option.rect.left() + (m_Tree && index.column() == 0 ? 24 : 0), option.rect.bottom(), option.rect.right(), option.rect.bottom());
+ painter->restore();
+ }
+ }
+
+ bool m_Grid;
+ bool m_Tree;
+ QColor m_Color;
+};
\ No newline at end of file
diff --git a/SandboxiePlus/MiscHelpers/Common/TreeWidgetEx.h b/SandboxiePlus/MiscHelpers/Common/TreeWidgetEx.h
new file mode 100644
index 00000000..6ee272b5
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/TreeWidgetEx.h
@@ -0,0 +1,150 @@
+#pragma once
+
+#include "../mischelpers_global.h"
+
+class MISCHELPERS_EXPORT QTreeWidgetEx: public QTreeWidget
+{
+ Q_OBJECT
+public:
+ QTreeWidgetEx(QWidget *parent = 0) : QTreeWidget(parent)
+ {
+ setUniformRowHeights(true);
+
+ m_AutoFitMax = 0;
+ m_ColumnReset = 1;
+
+ header()->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(header(), SIGNAL(customContextMenuRequested( const QPoint& )), this, SLOT(OnMenuRequested(const QPoint &)));
+
+ m_pMenu = new QMenu(this);
+
+ // Important: if something is shown/hidden we need a new size
+ connect(this->model(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SLOT(OnExpandCollapsed()));
+ connect(this->model(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)), this, SLOT(OnExpandCollapsed()));
+ //connect(this->header(), SIGNAL(geometriesChanged()), this, SLOT(OnExpandCollapsed())); // fixme
+ connect(this, SIGNAL(expanded(const QModelIndex &)), this, SLOT(OnExpandCollapsed()));
+ connect(this, SIGNAL(collapsed(const QModelIndex &)), this, SLOT(OnExpandCollapsed()));
+ }
+
+ static void AddSubItem(QTreeWidgetItem* pRoot, const QString& Key, const QString& Value)
+ {
+ QTreeWidgetItem* pItem = new QTreeWidgetItem(QStringList(Key));
+ pItem->setText(1, Value);
+ pRoot->addChild(pItem);
+ }
+
+ // Use this function to make the widget autosize to show all items up to this value
+ void setAutoFitMax(int AutoFitMax)
+ {
+ m_AutoFitMax = AutoFitMax;
+ }
+
+ void setColumnReset(int iMode)
+ {
+ m_ColumnReset = iMode;
+ }
+
+ QSize sizeHint() const {return m_AutoFitMax ? MySize() : QTreeWidget::sizeHint(); };
+ QSize minimumSizeHint() const { return m_AutoFitMax ? MySize() : QTreeWidget::sizeHint(); };
+
+signals:
+ void ResetColumns();
+
+public slots:
+ void OnResetColumns()
+ {
+ for (int i = 0; i < columnCount(); i++)
+ setColumnHidden(i, false);
+ }
+
+private slots:
+ void OnMenuRequested(const QPoint &point)
+ {
+ if(m_Columns.isEmpty())
+ {
+ QTreeWidgetItem* pHeader = headerItem();
+ for(int i=0; i < columnCount(); i++)
+ {
+ QAction* pAction = new QAction(pHeader->text(i), m_pMenu);
+ pAction->setCheckable(true);
+ connect(pAction, SIGNAL(triggered()), this, SLOT(OnMenu()));
+ m_pMenu->addAction(pAction);
+ m_Columns[pAction] = i;
+ }
+
+ if (m_ColumnReset)
+ {
+ m_pMenu->addSeparator();
+ QAction* pAction = m_pMenu->addAction(tr("Reset columns"));
+ if(m_ColumnReset == 1)
+ connect(pAction, SIGNAL(triggered()), this, SLOT(OnResetColumns()));
+ else
+ connect(pAction, SIGNAL(triggered()), this, SIGNAL(ResetColumns()));
+ }
+ }
+
+ for(QMap::iterator I = m_Columns.begin(); I != m_Columns.end(); I++)
+ I.key()->setChecked(!isColumnHidden(I.value()));
+
+ m_pMenu->popup(QCursor::pos());
+ }
+
+ void OnMenu()
+ {
+ QAction* pAction = (QAction*)sender();
+ int Column = m_Columns.value(pAction, -1);
+ setColumnHidden(Column, !pAction->isChecked());
+ QTimer::singleShot(10, this, SLOT(OnExpandCollapsed()));
+ }
+
+ void OnExpandCollapsed()
+ {
+ if (m_AutoFitMax)
+ updateGeometry();
+ }
+
+protected:
+ QSize MySize() const
+ { //QSize tst(sizeHintForColumn(0) + 2 * frameWidth(), sizeHintForRow(0) + 2 * frameWidth());
+
+ int neededHight= 2 * frameWidth()+ this->header()->height();
+
+ QAbstractItemModel* m = this->model();
+
+ QModelIndex root = this->rootIndex();
+ //if(this->rootIsDecorated())
+ neededHight += recursiveHeightHint(root,m);
+
+ if (this->horizontalScrollBar()->isVisible())
+ neededHight += this->horizontalScrollBar()->height();
+
+ if (neededHight > m_AutoFitMax)
+ neededHight = m_AutoFitMax;
+
+ QSize temp = QTreeView::sizeHint();
+ temp.setHeight(neededHight);
+ return QSize(1,neededHight);
+ }
+
+ // we need the size of all visible items -> isExpanded
+ // the root item is usually shown as a non-Valid index -> !i.isValid()
+ int recursiveHeightHint(QModelIndex i,QAbstractItemModel* m) const
+ {
+ int temp=sizeHintForIndex(i).height() + 1;
+ if(this->isExpanded(i) || !i.isValid())
+ {
+ if(m->hasChildren(i))
+ {
+ int numRows = m->rowCount(i);
+ for(int count =0;countindex(count,0,i),m);
+ }
+ }
+ return temp;
+ }
+
+ QMenu* m_pMenu;
+ QMap m_Columns;
+ int m_AutoFitMax;
+ int m_ColumnReset;
+};
diff --git a/SandboxiePlus/MiscHelpers/Common/Xml.cpp b/SandboxiePlus/MiscHelpers/Common/Xml.cpp
new file mode 100644
index 00000000..010ede48
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/Xml.cpp
@@ -0,0 +1,289 @@
+#include "stdafx.h"
+#include "Xml.h"
+
+QString CXml::Serialize(const QVariant &Variant, bool bLazy)
+{
+ QString String;
+ QXmlStreamWriter xml(&String);
+ Serialize(Variant, xml, bLazy);
+ return String;
+}
+
+QVariant CXml::Parse(const QString& String, bool bLazy)
+{
+ QXmlStreamReader xml(String);
+ return Parse(xml, bLazy);
+}
+
+void CXml::Serialize(const QVariant& Variant, QFile* pFile)
+{
+ QXmlStreamWriter xml(pFile);
+ Serialize(Variant, xml);
+}
+
+QVariant CXml::Parse(QFile* pFile)
+{
+ QXmlStreamReader xml(pFile);
+ return Parse(xml);
+}
+
+void CXml::Write(const QVariant& Variant, const QString& FileName)
+{
+ QFile File(FileName + ".tmp");
+ File.open(QFile::WriteOnly);
+ Serialize(Variant, &File);
+
+ QFile::rename(FileName, FileName + ".bak");
+ File.rename(FileName);
+ File.remove(FileName + ".bak");
+}
+
+QVariant CXml::Read(const QString& FileName)
+{
+ QFile File(FileName);
+ File.open(QFile::ReadOnly);
+ return Parse(&File);
+}
+
+void CXml::Serialize(const QVariant& Variant, QXmlStreamWriter &xml, bool bLazy)
+{
+#ifdef _DEBUG
+ xml.setAutoFormatting(true);
+#endif
+ xml.writeStartDocument();
+ Serialize("Variant", Variant, xml, bLazy);
+ xml.writeEndDocument();
+}
+
+QVariant CXml::Parse(QXmlStreamReader &xml, bool bLazy)
+{
+ QVariant Variant;
+ QString Temp;
+ Parse(Temp, Variant, xml, bLazy);
+ return Variant;
+}
+
+void CXml::Serialize(const QString& Name, const QVariant& Variant, QXmlStreamWriter &xml, bool bLazy)
+{
+ xml.writeStartElement(Name);
+ xml.writeAttribute("Type", GetTypeStr(Variant.type()));
+ switch(Variant.type())
+ {
+ case QVariant::Map:
+ {
+ QVariantMap Map = Variant.toMap();
+ for(QVariantMap::iterator I = Map.begin(); I != Map.end(); ++I)
+ Serialize(I.key(), I.value(), xml, bLazy);
+ break;
+ }
+ case QVariant::Hash:
+ {
+ QVariantHash Hash = Variant.toHash();
+ for(QVariantHash::iterator I = Hash.begin(); I != Hash.end(); ++I)
+ Serialize(I.key(), I.value(), xml, bLazy);
+ break;
+ }
+ case QVariant::List:
+ {
+ QVariantList List = Variant.toList();
+ for(QVariantList::iterator I = List.begin(); I != List.end(); ++I)
+ Serialize("Variant", *I, xml, bLazy);
+ break;
+ }
+ case QVariant::StringList:
+ {
+ QStringList List = Variant.toStringList();
+ for(QStringList::iterator I = List.begin(); I != List.end(); ++I)
+ Serialize("Variant", *I, xml, bLazy);
+ break;
+ }
+ case QVariant::ByteArray:
+ if(!bLazy)
+ {
+ xml.writeCharacters (Variant.toByteArray().toBase64());
+ break;
+ }
+ default:
+ ASSERT(Variant.canConvert(QVariant::String));
+ xml.writeCharacters(Variant.toString().toUtf8().toPercentEncoding(" :;/|,'+()"));
+ //xml.writeCharacters (Variant.toString().replace("\\","\\\\").replace("\r","\\r").replace("\n","\\n"));
+ break;
+ case QVariant::Invalid:
+ break;
+ }
+ xml.writeEndElement();
+}
+
+bool CXml::Parse(QString &Name, QVariant &Variant, QXmlStreamReader &xml, bool bLazy)
+{
+ bool bOpen = false;
+ QVariant::Type eType = QVariant::Invalid;
+ QString Text;
+ while (!xml.atEnd())
+ {
+ xml.readNext();
+ if (xml.error())
+ break;
+ if (xml.isEndDocument())
+ continue;
+
+ if (xml.isStartElement())
+ {
+ bOpen = true;
+ Name = xml.name().toString();
+ eType = GetType(xml.attributes().value("Type").toString());
+ QString Temp;
+ QVariant Item;
+ switch(eType)
+ {
+ case QVariant::Map:
+ {
+ QVariantMap Map;
+ while(Parse(Temp, Item, xml, bLazy))
+ Map.insert(Temp, Item);
+ Variant = Map;
+ return true;
+ }
+ case QVariant::Hash:
+ {
+ QVariantHash Hash;
+ while(Parse(Temp, Item, xml, bLazy))
+ Hash.insert(Temp, Item);
+ Variant = Hash;
+ return true;
+ }
+ case QVariant::List:
+ {
+ QVariantList List;
+ while(Parse(Temp, Item, xml, bLazy))
+ List.append(Item);
+ Variant = List;
+ return true;
+ }
+ case QVariant::StringList:
+ {
+ QStringList List;
+ while(Parse(Temp, Item, xml, bLazy))
+ List.append(Item.toString());
+ Variant = List;
+ return true;
+ }
+ }
+ }
+ else if (xml.isCharacters())
+ {
+ if(bOpen)
+ Text.append(xml.text().toString());
+ }
+ else if (xml.isEndElement())
+ {
+ if(bOpen)
+ {
+ if(eType == QVariant::ByteArray && !bLazy)
+ Variant.setValue(QByteArray::fromBase64(Text.toLatin1()));
+ else
+ {
+ /*bool bEsc = false;
+ for(int i = 0; i < Text.size(); i++)
+ {
+ if(bEsc) // ESC sequence handling
+ {
+ switch(Text.at(i).unicode())
+ {
+ case L'\\': Text.replace(--i,2,"\\"); break;
+ case L'r': Text.replace(--i,2,"\r"); break;
+ case L'n': Text.replace(--i,2,"\n"); break;
+ default: Text.replace(--i,2,"?"); break;
+ }
+ bEsc = false;
+ }
+ else if(Text.at(i) == L'\\')
+ bEsc = true;
+ }*/
+
+ Variant = QString::fromUtf8(QByteArray::fromPercentEncoding(Text.toLatin1()));
+ if(eType) // type is optional
+ {
+ ASSERT(Variant.canConvert(eType));
+ Variant.convert(eType);
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+ }
+
+ //ASSERT(0); // incomplete XML
+ return false;
+}
+
+
+/* SQVariants provides a lookup list of all known QVariant types for to/from string conversion
+*
+* Note: All commented out types dont have native to string converion
+* If there is a need to use them a manual conversion must be implemented in CXml
+*/
+struct SQVariants{
+ SQVariants()
+ {
+ Map.insert("Invalid" , QVariant::Invalid);
+
+ Map.insert("Bool" , QVariant::Bool);
+ Map.insert("Int" , QVariant::Int);
+ Map.insert("UInt" , QVariant::UInt);
+ Map.insert("LongLong" , QVariant::LongLong);
+ Map.insert("ULongLong" , QVariant::ULongLong);
+ Map.insert("Double" , QVariant::Double);
+ Map.insert("Char" , QVariant::Char);
+ Map.insert("Map" , QVariant::Map); // conainter type
+ Map.insert("List" , QVariant::List); // conainter type
+ Map.insert("String" , QVariant::String);
+ Map.insert("StringList" , QVariant::StringList); // conainter type
+ Map.insert("ByteArray" , QVariant::ByteArray);
+ //Map.insert("BitArray" , QVariant::BitArray);
+ Map.insert("Date" , QVariant::Date);
+ Map.insert("Time" , QVariant::Time);
+ Map.insert("DateTime" , QVariant::DateTime);
+ Map.insert("Url" , QVariant::Url);
+ /*Map.insert("Locale" , 18);
+ Map.insert("Rect" , 19);
+ Map.insert("RectF" , 20);
+ Map.insert("Size" , 21);
+ Map.insert("SizeF" , 22);
+ Map.insert("Line" , 23);
+ Map.insert("LineF" , 24);
+ Map.insert("Point" , 25);
+ Map.insert("PointF" , 26);
+ Map.insert("RegExp" , 27);*/
+ Map.insert("Hash" , QVariant::Hash); // conainter type
+
+ /*Map.insert("Font" , 64);
+ Map.insert("Pixmap" , 65);
+ Map.insert("Brush" , 66);
+ Map.insert("Color" , 67);
+ Map.insert("Palette" , 68);
+ Map.insert("Icon" , 69);
+ Map.insert("Image" , 70);
+ Map.insert("Polygon" , 71);
+ Map.insert("Region" , 72);
+ Map.insert("Bitmap" , 73);
+ Map.insert("Cursor" , 74);
+ Map.insert("SizePolicy" , 75);
+ Map.insert("KeySequence", 76);
+ Map.insert("Pen" , 77);
+ Map.insert("TextLength" , 78);
+ Map.insert("TextFormat" , 79);
+ Map.insert("Matrix" , 80);
+ Map.insert("Transform" , 81);
+ Map.insert("Matrix4x4" , 82);
+ Map.insert("Vector2D" , 83);
+ Map.insert("Vector3D" , 84);
+ Map.insert("Vector4D" , 85);
+ Map.insert("Quaternion" , 86);*/
+ }
+ QMap Map;
+} SQVariants;
+
+QString CXml::GetTypeStr(int Type) {return SQVariants.Map.key(Type, "Invalid");}
+QVariant::Type CXml::GetType(QString Type) {return (QVariant::Type)SQVariants.Map.value(Type, 0);}
\ No newline at end of file
diff --git a/SandboxiePlus/MiscHelpers/Common/Xml.h b/SandboxiePlus/MiscHelpers/Common/Xml.h
new file mode 100644
index 00000000..84ab07a1
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/Common/Xml.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "../mischelpers_global.h"
+
+class MISCHELPERS_EXPORT CXml {
+public:
+ static QString Serialize(const QVariant& Variant, bool bLazy = false);
+ static QVariant Parse(const QString& String, bool bLazy = false);
+
+ static void Serialize(const QVariant& Variant, QFile* pFile);
+ static QVariant Parse(QFile* pFile);
+
+ static void Write(const QVariant& Variant, const QString& FileName);
+ static QVariant Read(const QString& FileName);
+
+protected:
+ static void Serialize(const QVariant& Variant, QXmlStreamWriter &xml, bool bLazy = false);
+ static QVariant Parse(QXmlStreamReader &xml, bool bLazy = false);
+
+ static void Serialize(const QString& Name, const QVariant& Variant, QXmlStreamWriter &xml, bool bLazy = false);
+ static bool Parse(QString &Name, QVariant &Variant, QXmlStreamReader &xml, bool bLazy = false);
+
+ static QString GetTypeStr(int Type);
+ static QVariant::Type GetType(QString Type);
+};
diff --git a/SandboxiePlus/MiscHelpers/MiscHelpers.cpp b/SandboxiePlus/MiscHelpers/MiscHelpers.cpp
new file mode 100644
index 00000000..b1d8872b
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/MiscHelpers.cpp
@@ -0,0 +1,6 @@
+#include "stdafx.h"
+#include "MiscHelpers.h"
+
+MiscHelpers::MiscHelpers()
+{
+}
diff --git a/SandboxiePlus/MiscHelpers/MiscHelpers.h b/SandboxiePlus/MiscHelpers/MiscHelpers.h
new file mode 100644
index 00000000..0bbb1026
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/MiscHelpers.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include "mischelpers_global.h"
+
+#include ".\Common\CheckableMessageBox.h"
+#include ".\Common\ComboInputDialog.h"
+#include ".\Common\Common.h"
+#include ".\Common\DebugHelpers.h"
+#include ".\Common\ExitDialog.h"
+#include ".\Common\Finder.h"
+#include ".\Common\FlexError.h"
+#include ".\Common\FlowLayout.h"
+#include ".\Common\HistoryGraph.h"
+#include ".\Common\ItemChooser.h"
+#include ".\Common\KeyValueInputDialog.h"
+#include ".\Common\ListItemModel.h"
+#include ".\Common\MultiLineInputDialog.h"
+#include ".\Common\PanelView.h"
+#include ".\Common\ProgressDialog.h"
+#include ".\Common\Settings.h"
+#include ".\Common\SettingsWidgets.h"
+#include ".\Common\SmartGridWidget.h"
+#include ".\Common\SortFilterProxyModel.h"
+#include ".\Common\SplitTreeView.h"
+#include ".\Common\TabPanel.h"
+#include ".\Common\TreeItemModel.h"
+#include ".\Common\TreeViewEx.h"
+#include ".\Common\TreeWidgetEx.h"
+#include ".\Common\Xml.h"
+
+class MISCHELPERS_EXPORT MiscHelpers
+{
+public:
+ MiscHelpers();
+};
diff --git a/SandboxiePlus/MiscHelpers/MiscHelpers.vcxproj b/SandboxiePlus/MiscHelpers/MiscHelpers.vcxproj
new file mode 100644
index 00000000..06f973e1
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/MiscHelpers.vcxproj
@@ -0,0 +1,166 @@
+
+
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ {7AB8215A-59A4-4B8B-8090-16C87A860429}
+ QtVS_v302
+ 10.0.17763.0
+ $(MSBuildProjectDirectory)\QtMsBuild
+
+
+
+ DynamicLibrary
+ v140
+ false
+
+
+ DynamicLibrary
+ v141
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ msvc2015_64
+ core;network;widgets;winextras
+
+
+ msvc2017_64_dbg
+ core;network;widgets;winextras
+
+
+
+
+
+
+ stdafx.h
+
+
+ true
+ Disabled
+ ProgramDatabase
+ MultiThreadedDebugDLL
+ true
+ Use
+ stdafx.h
+ $(IntDir)$(TargetName).pch
+ MISCHELPERS_LIB;%(PreprocessorDefinitions)
+
+
+ Windows
+ $(OutDir)\$(ProjectName).dll
+ true
+
+
+
+
+ stdafx.h
+
+
+ true
+
+ MultiThreadedDLL
+ true
+ Use
+ stdafx.h
+ $(IntDir)$(TargetName).pch
+ MISCHELPERS_LIB;%(PreprocessorDefinitions)
+
+
+ Windows
+ $(OutDir)\$(ProjectName).dll
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Create
+ Create
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SandboxiePlus/MiscHelpers/MiscHelpers.vcxproj.filters b/SandboxiePlus/MiscHelpers/MiscHelpers.vcxproj.filters
new file mode 100644
index 00000000..4cecefc0
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/MiscHelpers.vcxproj.filters
@@ -0,0 +1,184 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;hm;inl;inc;xsd
+
+
+ {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E}
+ qrc;*
+ false
+
+
+ {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E}
+ qrc;*
+ false
+
+
+ {964acff8-ecee-4dfc-939d-e2406a533100}
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+
+
+ Header Files
+
+
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+ Common
+
+
+
\ No newline at end of file
diff --git a/SandboxiePlus/MiscHelpers/MiscHelpers.vcxproj.user b/SandboxiePlus/MiscHelpers/MiscHelpers.vcxproj.user
new file mode 100644
index 00000000..6e2aec7a
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/MiscHelpers.vcxproj.user
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/SandboxiePlus/MiscHelpers/mischelpers_global.h b/SandboxiePlus/MiscHelpers/mischelpers_global.h
new file mode 100644
index 00000000..9f79c367
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/mischelpers_global.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include
+
+#ifndef BUILD_STATIC
+# if defined(MISCHELPERS_LIB)
+# define MISCHELPERS_EXPORT Q_DECL_EXPORT
+# else
+# define MISCHELPERS_EXPORT Q_DECL_IMPORT
+# endif
+#else
+# define MISCHELPERS_EXPORT
+#endif
diff --git a/SandboxiePlus/MiscHelpers/stdafx.cpp b/SandboxiePlus/MiscHelpers/stdafx.cpp
new file mode 100644
index 00000000..a27b824d
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/stdafx.cpp
@@ -0,0 +1 @@
+#include "stdafx.h"
diff --git a/SandboxiePlus/MiscHelpers/stdafx.h b/SandboxiePlus/MiscHelpers/stdafx.h
new file mode 100644
index 00000000..a1e24a1c
--- /dev/null
+++ b/SandboxiePlus/MiscHelpers/stdafx.h
@@ -0,0 +1,135 @@
+#pragma once
+
+#define _CRT_SECURE_NO_WARNINGS
+
+
+
+// std includes
+#include
+#include
+#include
+#include
+#include
+#include